YouTube-Player-iOS-Helper中YTPlayerView进度滑块无法到达终点
问题描述
在iOS平台使用YouTube-Player-iOS-Helper实现YTPlayerView播放器功能时,自定义进度滑块存在数值异常:视频已播放结束,但自定义滑块停在中途位置,数值无法到达最大值终点。
原始核心代码
class PlayerViewController: UIViewController, YTPlayerViewDelegate { var durationOfVideo: Double? lazy var rewindSlider: UISlider = { let slider = UISlider() slider.minimumTrackTintColor = .white slider.maximumTrackTintColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 0.7) slider.setThumbImage(UIImage(named: "Thumb_On_ProgressLine"), for: .normal) slider.translatesAutoresizingMaskIntoConstraints = false slider.addTarget(self, action: #selector(handleSliderChange), for: .valueChanged) return slider }() @objc func handleSliderChange() { print(rewindSlider.value) } // MARK: viewDidLoad override func viewDidLoad() { super.viewDidLoad() webView.delegate = self webView.load(withVideoId: "kbMqWXnpXcA", playerVars: ["playsinline": 1]) } // MARK: Player control func playerViewDidBecomeReady(_ playerView: YTPlayerView) { playerView.duration { (time, error) in print("duration: \(time)") self.durationOfVideo = time } } func playerView(_ playerView: YTPlayerView, didPlayTime playTime: Float) { guard durationOfVideo != nil else { return } self.rewindSlider.value = Float((Double(playTime) / durationOfVideo!)) } }
问题根因
代码存在两处核心逻辑漏洞:
- 缺失播放结束状态的处理:
playerView(_:didPlayTime:)是固定间隔(约1秒1次)的进度回调,受回调频率限制,视频播放到末尾时,最后一次回调返回的playTime永远小于实际总时长,不会触发进度值等于1.0的场景,滑块自然无法到达终点。 - 总时长获取时机错误:
playerViewDidBecomeReady回调触发时,播放器刚完成初始化,此时调用duration方法拿到的视频总时长经常存在偏差(多数场景下会大于实际可播放时长),用这个偏差值做分母计算进度,哪怕视频实际播完,计算出的进度值也会小于1,进一步导致滑块停在中途。
修复方案
- 实现
playerView(_:didChangeTo:)状态回调代理:- 当状态切换为
.playing时,重新拉取一次准确的视频总时长,覆盖之前初始化阶段拿到的不准的数值; - 当状态切换为
.ended时,手动将rewindSlider的值设为1.0,直接将滑块拉到终点。
- 当状态切换为
- 在
didPlayTime的进度计算逻辑里增加边界截断,计算出的进度值如果大于1,直接赋值为1,避免异常值导致滑块越界。
修复后的核心代码参考:
// 补充状态变化代理方法 func playerView(_ playerView: YTPlayerView, didChangeTo state: YTPlayerState) { switch state { case .playing: // 播放状态下重新获取准确总时长 playerView.duration { [weak self] time, error in guard error == nil, time > 0 else { return } self?.durationOfVideo = time } case .ended: // 播放结束直接把滑块拉满 self.rewindSlider.value = 1.0 default: break } } func playerView(_ playerView: YTPlayerView, didPlayTime playTime: Float) { guard let totalDuration = durationOfVideo, totalDuration > 0 else { return } let progress = min(Float(playTime) / Float(totalDuration), 1.0) self.rewindSlider.value = progress }
内容的提问来源于stack exchange,提问作者iOS Swift-man Junior
相关产品推荐
相关产品推荐

