当应用程序从背景中回来时,Swift iOS -AVPlayervideo冻结/暂停

我在我的应用程序的登录页面上循环播放video。 我按照这个Youtube教程让它在视图控制器中运行循环video

问题是当应用程序进入后台时,如果我不立即回来,当我回来时,video会被冻结。

根据应该发生的Apple Docs 。

我试图使用NotificationCenter的Notification.Name.UIApplicationWillResignActive但这不起作用。

应用程序从后台返回后,如何让video继续播放?

 var player: AVPlayer! var playerLayer: AVPlayerLayer! override func viewDidLoad() { super.viewDidLoad() configurePlayer() } @objc fileprivate func configurePlayer(){ let url = Bundle.main.url(forResource: "myVideo", withExtension: ".mov") player = AVPlayer.init(url: url!) playerLayer = AVPlayerLayer(player: player!) playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill playerLayer.frame = view.layer.frame player.actionAtItemEnd = AVPlayerActionAtItemEnd.none player.play() view.layer.insertSublayer(playerLayer, at: 0) NotificationCenter.default.addObserver(self, selector: #selector(playerItemReachedEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem) NotificationCenter.default.addObserver(self, selector: #selector(playerItemReachedEnd), name: Notification.Name.UIApplicationWillResignActive, object: player.currentItem) } @objc fileprivate func playerItemReachedEnd(){ player.seek(to: kCMTimeZero) } 

根据Apple Docs播放video并将应用程序发送到后台时,播放器会自动暂停:

在此处输入图像描述

他们要做的是在应用程序进入后台时删除AVPlayerLayer (设置为nil),然后在前台进行重新初始化时:

在此处输入图像描述

他们说处理这个的最好方法是在applicationDidEnterBackgroundapplicationDidBecomeActive

在此处输入图像描述

我使用NSNotification来监听背景和前景事件,并设置函数暂停播放器并将playerLayer设置为nil(均为背景事件),然后重新初始化playerLayer并为前景事件播放播放器。 这些是我使用的通知.UIApplicationWillEnterForeground.UIApplicationDidEnterBackground

我发现的是,出于某种原因,如果你长按主页按钮,弹出的屏幕显示“我可以帮助你”,如果你再次按下主页按钮返回到你的应用video将被冻结,并使用上面的2通知不会阻止它。 我发现阻止这种情况的唯一方法是使用Notifications .UIApplicationWillResignActive.UIApplicationDidBecomeActive 。 如果除了上述通知之外不添加这些内容,那么您的video将在长按按住主页按钮后冻结。 我发现阻止所有冻结方案的最佳方法是使用所有4个通知。

我必须做的两件事与上面的代码不同的是将player和playerLayer类变量设置为可选项而不是隐式展开的选项,我还在AVPlayer类中添加了一个扩展,以检查它是否在iOS 9或更低版本中播放。 在iOS 10及更高版本中,有一个内置方法.timeControlStatus AVPlayer计时器状态

我上面的代码:

 var player: AVPlayer? var playerLayer: AVPlayerLayer? 

添加扩展到AVPlayer以检查 iOS 9或更低版本中AVPlayer的状态 :

 import AVFoundation extension AVPlayer{ var isPlaying: Bool{ return rate != 0 && error == nil } } 

以下是完整的代码:

 var player: AVPlayer? var playerLayer: AVPlayerLayer? //must be optional because it will get set to nil on background event override func viewDidLoad() { super.viewDidLoad() // background event NotificationCenter.default.addObserver(self, selector: #selector(setPlayerLayerToNil), name: .UIApplicationDidEnterBackground, object: nil) // foreground event NotificationCenter.default.addObserver(self, selector: #selector(reinitializePlayerLayer), name: .UIApplicationWillEnterForeground, object: nil) // add these 2 notifications to prevent freeze on long Home button press and back NotificationCenter.default.addObserver(self, selector: #selector(setPlayerLayerToNil), name: Notification.Name.UIApplicationWillResignActive, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(reinitializePlayerLayer), name: Notification.Name.UIApplicationDidBecomeActive, object: nil) configurePlayer() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // this is also for the long Home button press if let player = player{ if #available(iOS 10.0, *) { if player.timeControlStatus == .paused{ player.play() } } else { if player.isPlaying == false{ player.play() } } } } @objc fileprivate func configurePlayer(){ let url = Bundle.main.url(forResource: "myVideo", withExtension: ".mov") player = AVPlayer.init(url: url!) playerLayer = AVPlayerLayer(player: player!) playerLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill playerLayer?.frame = view.layer.frame player?.actionAtItemEnd = AVPlayerActionAtItemEnd.none player?.play() view.layer.insertSublayer(playerLayer!, at: 0) NotificationCenter.default.addObserver(self, selector: #selector(playerItemReachedEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem) } @objc fileprivate func playerItemReachedEnd(){ // this works like a rewind button. It starts the player over from the beginning player?.seek(to: kCMTimeZero) } // background event @objc fileprivate func setPlayerLayerToNil(){ // first pause the player before setting the playerLayer to nil. The pause works similar to a stop button player?.pause() playerLayer = nil } // foreground event @objc fileprivate func reinitializePlayerLayer(){ if let player = player{ playerLayer = AVPlayerLayer(player: player) if #available(iOS 10.0, *) { if player.timeControlStatus == .paused{ player.play() } } else { // if app is running on iOS 9 or lower if player.isPlaying == false{ player.play() } } } } 

不要忘记向AVPlayer添加isPlaying EXTENSION

添加观察者

 func addPlayerNotifications() { NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidPlayToEnd), name: .AVPlayerItemDidPlayToEndTime, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(applicationWillEnterForeground), name: .UIApplicationWillEnterForeground, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground), name: .UIApplicationDidEnterBackground, object: nil) } 

删除观察者

 func removePlayerNotifations() { NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: nil) NotificationCenter.default.removeObserver(self, name: .UIApplicationWillEnterForeground, object: nil) NotificationCenter.default.removeObserver(self, name: .UIApplicationDidEnterBackground, object: nil) } 

方法

 // Player end. @objc func playerItemDidPlayToEnd(_ notification: Notification) { // Your Code. player.seek(to: kCMTimeZero) } //App enter in forground. @objc func applicationWillEnterForeground(_ notification: Notification) { player.play() } //App enter in forground. @objc func applicationDidEnterBackground(_ notification: Notification) { player.pause() } 

试试这个代码

接受的答案对我不起作用。 我的“欢迎”video在某些场合随机暂停。

这是做了什么:
背景 :由于当应用程序“resignsActive”或进入“背景”(可以通过在调用各自的通知时观察其状态来validation)播放器和playerLayer对象不会被销毁,我猜测将这些对象中的任何一个设置为零然后在进入背景或前景时重新初始化它们有点不必要。

我只会在进入前景时再次播放播放器对象。

 var player: AVPlayer? var playerLayer: AVPlayerLayer? 

在ViewDidLoad中,我配置了我的播放器对象。

 override func viewDidLoad() { configurePlayer() } 

configurePlayer()函数定义如下

 private func configurePlayer() { guard let URL = Bundle.main.url(forResource: "welcome", withExtension: ".mp4") else { return } player = AVPlayer.init(url: URL) playerLayer = AVPlayerLayer(player: player) playerLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill playerLayer?.frame = view.layer.frame player?.actionAtItemEnd = AVPlayerActionAtItemEnd.none playItem() setupPlayNotificationItems() } 

以下是辅助函数实现

 private func setupPlayNotificationItems() { NotificationCenter.default.addObserver(self, selector: #selector(restartPlayerItem), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player?.currentItem) NotificationCenter.default.addObserver(self, selector: #selector(playItem), name: .UIApplicationWillEnterForeground, object: nil) } @objc private func playItem() { // If you please, you can also restart the video here restartPlayerItem() player?.play() if let playerlayer = playerLayer { view.layer.insertSublayer(playerlayer, at: 0) } } @objc func restartPlayerItem() { player?.seek(to: kCMTimeZero) }