feat(ios): forward real fullscreen events from AVPlayer instead of guessing (#4509)

This commit is contained in:
Wojciech Ogrodowczyk
2025-06-11 09:33:38 +02:00
committed by GitHub
parent 45fd250adf
commit 88c20d1c06
2 changed files with 37 additions and 22 deletions

View File

@@ -31,6 +31,10 @@ protocol RCTPlayerObserverHandler: RCTPlayerObserverHandlerObjc {
func handlePictureInPictureEnter() func handlePictureInPictureEnter()
func handlePictureInPictureExit() func handlePictureInPictureExit()
func handleRestoreUserInterfaceForPictureInPictureStop() func handleRestoreUserInterfaceForPictureInPictureStop()
func handleWillEnterFullScreen()
func handleDidEnterFullScreen()
func handleWillExitFullScreen()
func handleDidExitFullScreen()
} }
// MARK: - RCTPlayerObserver // MARK: - RCTPlayerObserver
@@ -323,17 +327,32 @@ class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate, AVPla
} }
#if !os(tvOS) #if !os(tvOS)
func playerViewController(
_: AVPlayerViewController,
willBeginFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator
) {
self._handlers?.handleWillEnterFullScreen()
coordinator.animate(alongsideTransition: nil) { [weak self] context in
guard let self, !context.isCancelled else { return }
self._handlers?.handleDidEnterFullScreen()
}
}
func playerViewController( func playerViewController(
_: AVPlayerViewController, _: AVPlayerViewController,
willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator
) { ) {
self._handlers?.handleWillExitFullScreen()
// iOS automatically pauses videos after exiting fullscreen, // iOS automatically pauses videos after exiting fullscreen,
// but it's better if we resume playback // but it's better if we resume playback
let wasPlaying = player?.timeControlStatus == .playing let wasPlaying = player?.timeControlStatus == .playing
coordinator.animate(alongsideTransition: nil) { [weak self] context in coordinator.animate(alongsideTransition: nil) { [weak self] context in
guard let self, !context.isCancelled, wasPlaying else { return } guard let self, !context.isCancelled else { return }
self.player?.play() self._handlers?.handleDidExitFullScreen()
if wasPlaying == true {
self.player?.play()
}
} }
} }
#endif #endif

View File

@@ -55,7 +55,6 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
private var _fullscreenAutorotate = true private var _fullscreenAutorotate = true
private var _fullscreenOrientation: String = "all" private var _fullscreenOrientation: String = "all"
private var _fullscreenPlayerPresented = false private var _fullscreenPlayerPresented = false
private var _fullscreenUncontrolPlayerPresented = false // to call events switching full screen mode from player controls
private var _filterName: String! private var _filterName: String!
private var _filterEnabled = false private var _filterEnabled = false
private var _presentingViewController: UIViewController? private var _presentingViewController: UIViewController?
@@ -1682,25 +1681,6 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
guard let bounds = RCTVideoUtils.getCurrentWindow()?.bounds else { return } guard let bounds = RCTVideoUtils.getCurrentWindow()?.bounds else { return }
if !oldRect!.equalTo(newRect!) { if !oldRect!.equalTo(newRect!) {
// https://github.com/TheWidlarzGroup/react-native-video/issues/3085#issuecomment-1557293391
if newRect!.equalTo(bounds) {
RCTLog("in fullscreen")
if !_fullscreenUncontrolPlayerPresented {
_fullscreenUncontrolPlayerPresented = true
self.onVideoFullscreenPlayerWillPresent?(["target": self.reactTag as Any])
self.onVideoFullscreenPlayerDidPresent?(["target": self.reactTag as Any])
}
} else {
NSLog("not fullscreen")
if _fullscreenUncontrolPlayerPresented {
_fullscreenUncontrolPlayerPresented = false
self.onVideoFullscreenPlayerWillDismiss?(["target": self.reactTag as Any])
self.onVideoFullscreenPlayerDidDismiss?(["target": self.reactTag as Any])
}
}
if let reactVC = self.reactViewController() { if let reactVC = self.reactViewController() {
reactVC.view.frame = bounds reactVC.view.frame = bounds
reactVC.view.setNeedsLayout() reactVC.view.setNeedsLayout()
@@ -1708,6 +1688,22 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
} }
} }
func handleWillEnterFullScreen() {
self.onVideoFullscreenPlayerWillPresent?(["target": self.reactTag as Any])
}
func handleDidEnterFullScreen() {
self.onVideoFullscreenPlayerDidPresent?(["target": self.reactTag as Any])
}
func handleWillExitFullScreen() {
self.onVideoFullscreenPlayerWillDismiss?(["target": self.reactTag as Any])
}
func handleDidExitFullScreen() {
self.onVideoFullscreenPlayerDidDismiss?(["target": self.reactTag as Any])
}
@objc @objc
func handleDidFailToFinishPlaying(notification: NSNotification!) { func handleDidFailToFinishPlaying(notification: NSNotification!) {
guard onVideoError != nil else { return } guard onVideoError != nil else { return }