【发布时间】:2020-03-10 21:48:41
【问题描述】:
TL;DR
似乎无法使用绑定来告诉已包装的AVPlayer 停止——为什么不呢? Vlad 的“一个奇怪的技巧”对我有用,没有状态和绑定,但为什么呢?
另见
我的问题类似于this one,但该海报想包装AVPlayerViewController,并且我想以编程方式控制播放。
This guy 也想知道何时调用了updateUIView()。
会发生什么(控制台日志如下所示。)
使用这里显示的代码,
-
用户点击“去电影”
-
MovieView出现并播放视频 - 这是因为
updateUIView(_:context:)被调用了
-
-
用户点击“返回首页”
-
HomeView再次出现 - 播放停止
- 再次调用
updateUIView。 - 查看控制台日志 1
-
-
但是...删除
###这一行,然后- 即使返回主视图也会继续播放
-
updateUIView在抵达时被调用,但在离开时未被调用 - 查看控制台日志 2
-
如果您取消注释
%%%代码(并注释掉它前面的内容)- 您会得到我认为在逻辑和习惯上正确的 SwiftUI 代码...
- ...但是“它不起作用”。 IE。视频在抵达时播放,但在离开时继续播放。
- 查看控制台日志 3
代码
我确实使用@EnvironmentObject,所以正在进行一些状态共享。
主要内容视图(这里没有争议):
struct HomeView: View {
@EnvironmentObject var router: ViewRouter
var body: some View {
ZStack() { // +++ Weird trick ### fails if this is Group(). Wtf?
if router.page == .home {
Button(action: { self.router.page = .movie }) {
Text("Go to Movie")
}
} else if router.page == .movie {
MovieView()
}
}
}
}
它使用其中之一(仍然是常规的声明式 SwiftUI):
struct MovieView: View {
@EnvironmentObject var router: ViewRouter
// @State private var isPlaying: Bool = false // %%%
var body: some View {
VStack() {
PlayerView()
// PlayerView(isPlaying: $isPlaying) // %%%
Button(action: { self.router.page = .home }) {
Text("Go back Home")
}
}.onAppear {
print("> onAppear()")
self.router.isPlayingAV = true
// self.isPlaying = true // %%%
print("< onAppear()")
}.onDisappear {
print("> onDisappear()")
self.router.isPlayingAV = false
// self.isPlaying = false // %%%
print("< onDisappear()")
}
}
}
现在我们进入AVKit 特定的内容。我使用Chris Mash描述的方法。
前面提到的PlayerView,包装器:
struct PlayerView: UIViewRepresentable {
@EnvironmentObject var router: ViewRouter
// @Binding var isPlaying: Bool // %%%
private var myUrl : URL? { Bundle.main.url(forResource: "myVid", withExtension: "mp4") }
func makeUIView(context: Context) -> PlayerView {
PlayerUIView(frame: .zero , url : myUrl)
}
// ### This one weird trick makes OS call updateUIView when view is disappearing.
class DummyClass { } ; let x = DummyClass()
func updateUIView(_ v: PlayerView, context: UIViewRepresentableContext<PlayerView>) {
print("> updateUIView()")
print(" router.isPlayingAV = \(router.isPlayingAV)")
// print(" isPlaying = \(isPlaying)") // %%%
// This does work. But *only* with the Dummy code ### included.
// See also +++ comment in HomeView
if router.isPlayingAV { v.player?.pause() }
else { v.player?.play() }
// This logic looks reversed, but is correct.
// If it's the other way around, vid never plays. Try it!
// if isPlaying { v?.player?.play() } // %%%
// else { v?.player?.pause() } // %%%
print("< updateUIView()")
}
}
还有 wrappED UIView:
class PlayerUIView: UIView {
private let playerLayer = AVPlayerLayer()
var player: AVPlayer?
init(frame: CGRect, url: URL?) {
super.init(frame: frame)
guard let u = url else { return }
self.player = AVPlayer(url: u)
self.playerLayer.player = player
self.layer.addSublayer(playerLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
playerLayer.frame = bounds
}
required init?(coder: NSCoder) { fatalError("not implemented") }
}
当然还有视图路由器,基于Blckbirds 示例
class ViewRouter : ObservableObject {
let objectWillChange = PassthroughSubject<ViewRouter, Never>()
enum Page { case home, movie }
var page = Page.home { didSet { objectWillChange.send(self) } }
// Claim: App will never play more than one vid at a time.
var isPlayingAV = false // No didSet necessary.
}
控制台日志
控制台日志 1(根据需要播放停止)
> updateUIView() // First call
router.isPlayingAV = false // Vid is not playing => play it.
< updateUIView()
> onAppear()
< onAppear()
> updateUIView() // Second call
router.isPlayingAV = true // Vid is playing => pause it.
< updateUIView()
> onDisappear() // After the fact, we clear
< onDisappear() // the isPlayingAV flag.
控制台日志 2(奇怪的技巧被禁用;游戏继续)
> updateUIView() // First call
router.isPlayingAV = false
< updateUIView()
> onAppear()
< onAppear()
// No second call.
> onDisappear()
< onDisappear()
控制台日志 3(尝试使用状态和绑定;继续播放)
> updateUIView()
isPlaying = false
< updateUIView()
> onAppear()
< onAppear()
> updateUIView()
isPlaying = true
< updateUIView()
> updateUIView()
isPlaying = true
< updateUIView()
> onDisappear()
< onDisappear()
【问题讨论】:
标签: data-binding swiftui avplayer wrapper