You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Swift中如何通过通知等方式获取AVPlayerViewController原生事件回调?

嘿,我刚好在项目里处理过类似的需求,给你梳理几种靠谱的方案来捕获AVPlayerViewController的原生控件事件和状态变化:

1. 监听播放/暂停状态变化

AVPlayerViewController本身没有直接暴露播放/暂停的回调,但我们可以通过监听它内部AVPlayerrate属性来判断状态——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. 获取播放进度与当前时长追踪

要实时获取播放进度和时长,有两种常用方式:

方式一:定期时间观察者

通过AVPlayeraddPeriodicTimeObserver方法,可以按指定间隔获取当前播放时间:

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.timeControlStatusrate一起判断;
  • 不要试图直接访问AVPlayerViewController的私有子控件(比如播放按钮),苹果可能会在后续版本修改控件结构,导致代码崩溃。

内容的提问来源于stack exchange,提问作者Sumitava Datta

火山引擎 最新活动