You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法恢复画中画至原尺寸: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栈。通过以下纯代码修改即可解决:

  1. 复用AVPlayerViewController实例,避免每次点击按钮重复创建
  2. 实现AVPlayerViewControllerDelegate的恢复方法,手动重新呈现控制器
  3. 清理通知观察者,避免内存泄漏

修改后的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 13:10:10