无法恢复画中画至原尺寸:AVPlayerViewController最大化后消失
AVPlayerViewController画中画恢复时消失的纯代码解决方案
我通过按钮弹出AVPlayerViewController,已开启画中画功能可正常最小化,但从画中画恢复原尺寸时,控制器会直接消失。不想使用Storyboard,也不愿将控制器嵌入导航控制器,求纯代码解决办法。
已配置Background Mode的PIP权限,现有代码如下:
AppDelegate代码
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let audioSession = AVAudioSession.sharedInstance() do { try audioSession.setCategory(.playback) try audioSession.setActive(true, options: []) } catch { print("Setting category to AVAudioSessionCategoryPlayback failed.") } return true }
ViewController代码
class ViewController: UIViewController,AVPlayerViewControllerDelegate { var playerController : AVPlayerViewController! override func viewDidLoad() { super.viewDidLoad() } @IBAction func btnClicked(_ sender: Any) { if let videoURL = URL(string: "https://storage.googleapis.com/exoplayer-test-media-0/BigBuckBunny_320x180.mp4") { let player = AVPlayer(url: videoURL) playerController = AVPlayerViewController() NotificationCenter.default.addObserver(self, selector: #selector(ViewController.didfinishPlaying(note:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem) playerController.player = player playerController.allowsPictureInPicturePlayback = true playerController.delegate = self playerController.player?.play() self.present(playerController, animated: true, completion : nil) } } }
注:仅演示用了Storyboard,实际项目会纯代码实现。
解决方案
问题根源是画中画恢复时,系统无法正确重建原有的present栈。通过以下纯代码修改即可解决:
- 复用AVPlayerViewController实例,避免每次点击按钮重复创建
- 实现AVPlayerViewControllerDelegate的恢复方法,手动重新呈现控制器
- 清理通知观察者,避免内存泄漏
修改后的ViewController代码:
class ViewController: UIViewController, AVPlayerViewControllerDelegate { var playerController: AVPlayerViewController? var player: AVPlayer? override func viewDidLoad() { super.viewDidLoad() // 提前初始化播放器和控制器 setupPlayerController() } private func setupPlayerController() { guard let videoURL = URL(string: "https://storage.googleapis.com/exoplayer-test-media-0/BigBuckBunny_320x180.mp4") else { return } player = AVPlayer(url: videoURL) playerController = AVPlayerViewController() playerController?.player = player playerController?.allowsPictureInPicturePlayback = true playerController?.delegate = self // 添加播放结束通知 NotificationCenter.default.addObserver(self, selector: #selector(didfinishPlaying(note:)), name: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem) } @IBAction func btnClicked(_ sender: Any) { guard let playerVC = playerController else { return } // 判断控制器是否已在窗口中,不在则present,否则直接播放 if playerVC.view.window == nil { present(playerVC, animated: true) { self.player?.play() } } else { playerVC.player?.play() } } @objc func didfinishPlaying(note: NSNotification) { // 播放结束后重置播放位置 player?.seek(to: .zero) } // 实现画中画恢复代理方法,手动确保控制器被正确呈现 func playerViewController(_ playerViewController: AVPlayerViewController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) { if playerViewController.view.window == nil { present(playerViewController, animated: true) { completionHandler(true) } } else { completionHandler(true) } } deinit { // 移除通知观察者,防止内存泄漏 NotificationCenter.default.removeObserver(self) } }
修改说明
- 提前在
viewDidLoad中初始化播放器和控制器,避免重复创建导致的状态混乱 - 点击按钮时先检查控制器是否已显示,避免重复present
- 通过
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler方法,在画中画恢复时手动处理控制器的呈现逻辑,确保界面正常显示 - 添加
deinit方法清理通知观察者,避免内存泄漏
AppDelegate的音频会话配置无需修改,保持现有设置即可。
内容的提问来源于stack exchange,提问作者Chup bae
相关产品推荐
相关产品推荐

