根据Apple Docs,当正在播放视频并将应用发送到后台时,播放器会自动暂停:
他们说要做的是在应用程序进入后台时删除AVPlayerLayer(设置为nil),然后在进入前台时重新初始化它:
他们说处理这个问题的最好方法是applicationDidEnterBackground 和applicationDidBecomeActive:
我使用 NSNotification 来监听背景和前景事件,并设置函数来暂停播放器并将 playerLayer 设置为 nil(均用于背景事件),然后重新初始化 playerLayer 并为前景事件播放播放器。这些是我使用的通知 .UIApplicationWillEnterForeground 和 .UIApplicationDidEnterBackground
我发现,由于某种原因,如果您长按 Home 键,如果您再次按 Home 键离开,则会弹出“我能帮您什么”的屏幕回到您的应用程序,视频将被冻结,使用上面的 2 个通知不会阻止它。我发现防止这种情况发生的唯一方法是也使用通知.UIApplicationWillResignActive 和.UIApplicationDidBecomeActive。如果您在上述通知之外没有添加这些,那么您的视频将在主页按钮长按和返回时冻结。我发现防止所有冻结情况的最佳方法是使用所有 4 个通知。
我必须做的与上面的代码不同的两件事是将 player 和 playerLayer 类变量设置为可选项而不是隐式解包的可选项,我还向 AVPlayer 类添加了一个扩展,以检查它是否在 iOS 中播放9 或以下。在 iOS 10 及更高版本中有一个内置方法 .timeControlStatus AVPlayer timer status
我上面的代码:
var player: AVPlayer?
var playerLayer: AVPlayerLayer?
在 iOS 9 或更低版本中将 AVPlayer 的扩展添加到 check the state of the 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: UIApplication.didEnterBackgroundNotification, object: nil)
// foreground event
NotificationCenter.default.addObserver(self, selector: #selector(reinitializePlayerLayer), name: UIApplication.willEnterForegroundNotification, object: nil)
// add these 2 notifications to prevent freeze on long Home button press and back
NotificationCenter.default.addObserver(self, selector: #selector(setPlayerLayerToNil), name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(reinitializePlayerLayer), name: UIApplication.didBecomeActiveNotification, 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()
}
}
}
}
不要忘记将 isPlaying 扩展名添加到 AVPlayer