From 051e884c8f34755c887b66d8715a6ee38efc5f77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Kueny?= Date: Thu, 4 Apr 2024 15:08:48 +0200 Subject: [PATCH] fix(ios): call PictureInPicture callbacks with native controls (#3603) * fix(ios): call PictureInPictureStatusChanged callback with native controls We add RCTPlayerObserver as playerViewController delegate to be notified with PiP lifecycle should partially fix #3602 * fix(ios): call onRestoreUserInterfaceForPictureInPictureStop callback with native controls should partially fix #3602 --- ios/Video/Features/RCTPlayerObserver.swift | 39 +++++++++++++++++++++- ios/Video/RCTVideo.swift | 18 +++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/ios/Video/Features/RCTPlayerObserver.swift b/ios/Video/Features/RCTPlayerObserver.swift index 22db23a8..0e19571f 100644 --- a/ios/Video/Features/RCTPlayerObserver.swift +++ b/ios/Video/Features/RCTPlayerObserver.swift @@ -27,11 +27,14 @@ protocol RCTPlayerObserverHandler: RCTPlayerObserverHandlerObjc { func handleViewControllerOverlayViewFrameChange(overlayView: UIView, change: NSKeyValueObservedChange) func handleTracksChange(playerItem: AVPlayerItem, change: NSKeyValueObservedChange<[AVPlayerItemTrack]>) func handleLegibleOutput(strings: [NSAttributedString]) + func handlePictureInPictureEnter() + func handlePictureInPictureExit() + func handleRestoreUserInterfaceForPictureInPictureStop() } // MARK: - RCTPlayerObserver -class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate, AVPlayerItemLegibleOutputPushDelegate { +class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate, AVPlayerItemLegibleOutputPushDelegate, AVPlayerViewControllerDelegate { weak var _handlers: RCTPlayerObserverHandler? var player: AVPlayer? { @@ -105,6 +108,7 @@ class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate, AVPla private var _playerLayerReadyForDisplayObserver: NSKeyValueObservation? private var _playerViewControllerOverlayFrameObserver: NSKeyValueObservation? private var _playerTracksObserver: NSKeyValueObservation? + private var _restoreUserInterfaceForPIPStopCompletionHandler: ((Bool) -> Void)? deinit { if let _handlers { @@ -192,11 +196,14 @@ class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate, AVPla options: [.new, .old], changeHandler: _handlers.handleViewControllerOverlayViewFrameChange ) + + playerViewController.delegate = self } func removePlayerViewControllerObservers() { _playerViewControllerReadyForDisplayObserver?.invalidate() _playerViewControllerOverlayFrameObserver?.invalidate() + playerViewController?.delegate = nil } func addPlayerLayerObserver() { @@ -288,4 +295,34 @@ class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate, AVPla NotificationCenter.default.removeObserver(_handlers) } } + + func playerViewControllerDidStartPictureInPicture(_: AVPlayerViewController) { + guard let _handlers else { return } + + _handlers.handlePictureInPictureEnter() + } + + func playerViewControllerDidStopPictureInPicture(_: AVPlayerViewController) { + guard let _handlers else { return } + + _handlers.handlePictureInPictureExit() + } + + func playerViewController( + _: AVPlayerViewController, + restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void + ) { + guard let _handlers else { return } + + _handlers.handleRestoreUserInterfaceForPictureInPictureStop() + + _restoreUserInterfaceForPIPStopCompletionHandler = completionHandler + } + + func setRestoreUserInterfaceForPIPStopCompletionHandler(_ restore: Bool) { + guard let _restoreUserInterfaceForPIPStopCompletionHandler else { return } + + _restoreUserInterfaceForPIPStopCompletionHandler(restore) + self._restoreUserInterfaceForPIPStopCompletionHandler = nil + } } diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 8f8c7537..cb22e870 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -128,6 +128,18 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH onPictureInPictureStatusChanged?(["isActive": NSNumber(value: false)]) } + func handlePictureInPictureEnter() { + onPictureInPictureStatusChanged?(["isActive": NSNumber(value: true)]) + } + + func handlePictureInPictureExit() { + onPictureInPictureStatusChanged?(["isActive": NSNumber(value: false)]) + } + + func handleRestoreUserInterfaceForPictureInPictureStop() { + onRestoreUserInterfaceForPictureInPictureStop?([:]) + } + func isPipEnabled() -> Bool { return _pictureInPictureEnabled } @@ -606,7 +618,11 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH @objc func setRestoreUserInterfaceForPIPStopCompletionHandler(_ restore: Bool) { #if os(iOS) - _pip?.setRestoreUserInterfaceForPIPStopCompletionHandler(restore) + if _pip != nil { + _pip?.setRestoreUserInterfaceForPIPStopCompletionHandler(restore) + } else { + _playerObserver.setRestoreUserInterfaceForPIPStopCompletionHandler(restore) + } #endif }