Reputation: 5816
I have google's YTPlayerView leveraged wrapped in UIViewRepresentable in a swiftui project. When user leaves the view with YTPlayerView in progress audio keeps playing in the background. So I need to call stopVideo() on YTPlayerView wrapped instance when this happens. Given that UIViewRepresentable has no view lifecycle management callbacks that I know of I hooked up onDisappear in the View utilizing YTPlayerView as its child. However I get
Accessing StateObject's object without being installed on a View. This will create a new instance each time.
Cause stopVideo is called after the view is already gone. Is there an equivalent of UIKit viewWillDisappear to make the call then the view is still onscreen? ("installed on a View" in SwiftUI newspeak that is)
The ugliness:
struct YTPlayer: UIViewRepresentable, Stoppable {
internal init(observer: YTPlayerViewDelegateObserverProxy, videoID: String, model: PlayerViewModel) {
self.observer = observer
self.videoID = videoID
self._model = StateObject(wrappedValue: model)
model.ytplayer = self
}
@State var observer: YTPlayerViewDelegateObserverProxy
let videoID: String
static var cache = NSCache<NSString, YTPlayerView>()
@StateObject private var model: PlayerViewModel
func makeUIView(context: Context) -> YTPlayerView {
if let pv = YTPlayer.cache.object(forKey: videoID as NSString) {
return pv
}
let view = YTPlayerView()
YTPlayer.cache.setObject(view, forKey: videoID as NSString)
return view
}
func updateUIView(_ player: YTPlayerView, context: Context) {
guard player.delegate !== observer else {
return
}
model.ytplayerview = player
let playerVars = ["playsinline" : NSNumber(1)]
player.delegate = observer
player.load(withVideoId: videoID, playerVars: playerVars)
}
static func dismantleUIView(player: YTPlayerView, coordinator: Self.Coordinator)
{
player.stopVideo()
}
func stop() {
guard let player = model.ytplayerview as? YTPlayerView else {
assertionFailure()
return
}
player.stopVideo()
}
func anyview() -> AnyView {
AnyView(self)
}
}
Even more ugliness:
final public class PlayerViewModel: NSObject, ObservableObject {
...
public var ytplayerview: UIView?
public var ytplayer: Stoppable?
public protocol Stoppable
{
func stop()
// I know, pathetic, I moved it to the Lincoln street...
func anyview() -> AnyView
}
Upvotes: -1
Views: 454
Reputation: 30746
You need to upgrade to UIViewControllerRepresentable
with a custom UIViewController
subclass that has YTPlayerView
as its view to get access to viewWillDisappear
and viewDidDisappear
.
As of 18.4 beta the appearance methods is now received in representables inside containers like List, TabView etc.
Upvotes: 0