问题描述
因为我在单元格中播放视频,所以我有一个 AVPlayer 可以在某些情况下立即播放视频,而在其他情况下它会在几秒钟后运行。当它立即运行时,.status
工作正常。但是,当我将它包装在 dispatchWorkItem
中并带有 .asyncAfter
延迟时,永远不会调用完全相同的 .status
。我也尝试使用 perform(_:,with:,afterDelay:)
和 Timer
但这也不起作用。
var player: AVPlayer?
var playerItem: AVPlayerItem?
var observer: NSkeyvalueObservation? = nil
var workItem: dispatchWorkItem? = nil
var startImmediately = false
var timer: Timer?
viewDidLoad() {
// add asset to playerItem,add playerItem to player ...
}
func someCircumstance() {
if startImmediately {
setNSkeyvalueObserver() // this works fine and .status is called
} else {
createWorkItem()
dispatchQueue.main.asyncAfter(deadline: .Now() + 0.33,execute: executeWorkItem) // this delay runs but .status is never called
// perform(#selector(executeWorkItem),with: nil,afterDelay: 0.33) // same issue
// timer = Timer.scheduledTimer(timeInterval: 0.33,target: self,selector: #selector(executeWorkItem),userInfo: nil,repeats: false) // same issue
// RunLoop.current.add(timer!,forMode: .common)
}
}
func createWorkItem() {
workItem = dispatchWorkItem {
dispatchQueue.main.async { [weak self] in
self?.setNSkeyvalueObserver()
}
}
}
@objc func executeWorkItem() {
guard let workItem = workItem else { return }
workItem.perform()
}
func setNSkeyvalueObserver() {
// without a long explanation this sometimes has to start with a delay because of scrolling reason I also might have to cancel it
observer = player?.observe(\.status,options: [.new,.old]) { [weak self] (player,change) in
switch (player.status) {
case .readyToPlay:
print("Media Ready to Play")
case .Failed,.unkNown:
print("Media Failed to Play")
@unkNown default:
print("UnkNown Error")
}
}
}
我也尝试使用较旧的 KVO API .status 观察器,但在使用延迟时发生了同样的问题
private var keepUpContext = 0
viewDidLoad() {
// add asset to playerItem,add playerItem to player ...
player?.addobserver(self,forKeyPath: "currentItem.playbackLikelyToKeepUp",options: [.old,.new],context: &keepUpContext)
}
override func observeValue(forKeyPath keyPath: String?,of object: Any?,change: [NSkeyvalueChangeKey : Any]?,context: UnsafeMutableRawPointer?) {
if context == &keepUpContext {
if let player = player,let item = player.currentItem,item.isPlaybackLikelyToKeepUp {
print("Media Ready to Play")
}
}
}
问题似乎是延迟。
更新
我刚刚尝试了以下代码,但这也不起作用:
if startImmediately {
setNSkeyvalueObserver()
} else {
dispatchQueue.main.asyncAfter(deadline: .Now() + 0.15) { [weak self] in
self?.setNSkeyvalueObserver()
}
}
解决方法
看起来您是在播放器加载内容后添加您的观察者。它可能在 viewDidLoad
和延迟之间加载。如果在添加观察者时将 .initial
添加到选项列表中,即使玩家已经准备好,您也一定会收到状态通知。
您能否尝试将 forKeyPath
参数值更改为 #keyPath(AVPlayerItem.status)