Swift中如何通过通知等方式获取AVPlayerViewController原生事件回调?
嘿,我刚好在项目里处理过类似的需求,给你梳理几种靠谱的方案来捕获AVPlayerViewController的原生控件事件和状态变化:
1. 监听播放/暂停状态变化
AVPlayerViewController本身没有直接暴露播放/暂停的回调,但我们可以通过监听它内部AVPlayer的rate属性来判断状态——rate == 1表示正在播放,rate == 0表示暂停(注意:如果是因为缓冲导致的暂停,rate也会是0,需要结合timeControlStatus进一步区分)。
用KVO实现的代码示例:
class YourPlayerVC: AVPlayerViewController { private var observation: NSKeyValueObservation? override func viewDidLoad() { super.viewDidLoad() // 监听player的rate变化 observation = player?.observe(\.rate, options: [.new]) { [weak self] player, change in guard let newRate = change.newValue else { return } if newRate == 1 { print("用户触发了播放操作(或自动播放)") // 这里处理播放回调逻辑 } else if newRate == 0 { // 区分是用户暂停还是缓冲暂停 if player.timeControlStatus == .paused { print("用户触发了暂停操作") // 处理暂停回调逻辑 } else if player.timeControlStatus == .waitingToPlayAtSpecifiedRate { print("因为缓冲暂时暂停") } } } } deinit { observation?.invalidate() // 记得移除观察者,避免内存泄漏 } }
2. 获取播放进度与当前时长追踪
要实时获取播放进度和时长,有两种常用方式:
方式一:定期时间观察者
通过AVPlayer的addPeriodicTimeObserver方法,可以按指定间隔获取当前播放时间:
class YourPlayerVC: AVPlayerViewController { private var progressObserver: Any? override func viewDidLoad() { super.viewDidLoad() // 设置每0.5秒更新一次进度 progressObserver = player?.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(0.5, preferredTimescale: 1000), queue: .main) { [weak self] time in guard let player = self?.player, let currentItem = player.currentItem else { return } // 当前播放时长(秒) let currentTime = CMTimeGetSeconds(time) // 视频总时长(秒),注意刚开始加载时可能是NaN,需要判断 let totalDuration = CMTimeGetSeconds(currentItem.duration) if !totalDuration.isNaN { print("当前进度:\(currentTime)/\(totalDuration)") // 更新UI或处理进度逻辑 } } } deinit { if let observer = progressObserver { player?.removeTimeObserver(observer) } } }
方式二:监听AVPlayerItem的duration变化
如果只需要在时长加载完成或变化时获取(比如视频是直播流时长动态变化),可以用KVO监听currentItem.duration:
private var durationObservation: NSKeyValueObservation? override func viewDidLoad() { super.viewDidLoad() durationObservation = player?.currentItem?.observe(\.duration, options: [.new]) { [weak self] item, change in let totalDuration = CMTimeGetSeconds(item.duration) if !totalDuration.isNaN { print("视频总时长更新为:\(totalDuration)秒") } } }
3. 捕获播放结束(停止)操作
当用户点击原生控件的“播放结束”按钮,或者视频自然播放完成时,可以通过监听AVPlayerItemDidPlayToEndTime通知来捕获:
override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(handlePlaybackFinished), name: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem) } @objc private func handlePlaybackFinished() { print("视频播放结束(用户停止或自然结束)") // 处理停止回调逻辑 } deinit { NotificationCenter.default.removeObserver(self) }
额外注意事项
- 所有KVO和通知的监听都要记得在
deinit中移除,防止内存泄漏; - 尽量在主线程处理UI更新,避免界面卡顿;
- 如果需要区分用户手动操作和系统自动状态变化(比如缓冲暂停),可以结合
player.timeControlStatus和rate一起判断; - 不要试图直接访问AVPlayerViewController的私有子控件(比如播放按钮),苹果可能会在后续版本修改控件结构,导致代码崩溃。
内容的提问来源于stack exchange,提问作者Sumitava Datta




