TL; DR
Невозможно использовать привязку, чтобы сказать обернутому AVPlayer
остановиться - почему бы и нет? "Один странный трюк" из Влад работает для меня, без состояния и привязки, но почему?
См. Также
Мой вопрос похож на этот , но этот плакат хотел обернуть AVPlayerViewController
, и я хочу управлять воспроизведением программно .
Этот парень также задавался вопросом, когда был вызван updateUIView()
.
Что происходит (Журналы консоли показаны ниже.)
С кодом, показанным здесь,
Пользователь нажимает «Go на Mov ie»
MovieView
, и отображается видео - Это потому, что
updateUIView(_:context:)
вызывается
Пользователь нажимает "Go back Home"
HomeView
появляется - Воспроизведение останавливается
- Снова вызывается
updateUIView
. - См. Журнал консоли 1
Но ... удалить строку ###
и
- Воспроизведение продолжается, даже когда возвращается исходный вид
updateUIView
по прибытии, но не при отъезде - См. Журнал консоли 2
Если вы раскомментируете код %%%
(и прокомментируете, что ему предшествует)
- Вы получите код, который, как мне показалось, был логически и идиоматически правильным SwiftUI ...
- ... но «это не работает». Т.е. видео играет по прибытии, но продолжается при отправлении.
- См. Журнал консоли 3
Код
I do use @EnvironmentObject
так что - это происходит некоторое разделение состояний.
1099 * Основной вид контента (ничего спорно здесь):
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):
1104
Теперь мы попадаем в AVKit
-specifi c прочее. Я использую подход, описанный Крисом Ма sh.
Вышеупомянутым 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()")
}
}
И завернутый 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.
}
Журналы консоли
Консоль log 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.
Console log 2 (странный трюк отключен; воспроизведение продолжается)
> updateUIView() // First call
router.isPlayingAV = false
< updateUIView()
> onAppear()
< onAppear()
// No second call.
> onDisappear()
< onDisappear()
Console log 3 (попытка использовать состояние и привязку; воспроизведение продолжается )
> updateUIView()
isPlaying = false
< updateUIView()
> onAppear()
< onAppear()
> updateUIView()
isPlaying = true
< updateUIView()
> updateUIView()
isPlaying = true
< updateUIView()
> onDisappear()
< onDisappear()