如何为AVPlayer的UISlider添加并控制加载进度浅蓝线?
给AVPlayer关联的UISlider添加加载进度浅蓝线
原生UISlider没有直接提供加载进度的显示接口,我们可以通过添加自定义子视图的方式实现,以下是具体方案:
实现思路
在UISlider的Track层级下方,添加一个浅蓝背景的UIView作为加载进度条,通过AVPlayer的loadedTimeRanges获取已加载时长,计算比例后更新这个视图的宽度,从而实现加载进度的可视化。
代码实现(自定义Slider子类)
子类化UISlider可以让控件复用性更强,代码如下:
import UIKit import AVFoundation class AVPlayerProgressSlider: UISlider { // 加载进度条视图 private let loadedProgressView = UIView() override init(frame: CGRect) { super.init(frame: frame) setupProgressView() } required init?(coder: NSCoder) { super.init(coder: coder) setupProgressView() } private func setupProgressView() { // 设置浅蓝色(可根据需求调整) loadedProgressView.backgroundColor = UIColor(red: 100/255, green: 180/255, blue: 255/255, alpha: 0.6) loadedProgressView.clipsToBounds = true loadedProgressView.layer.cornerRadius = 2 // 和Slider的Track圆角保持一致 addSubview(loadedProgressView) // 将进度条放在Track下方,避免挡住滑块 sendSubviewToBack(loadedProgressView) } override func layoutSubviews() { super.layoutSubviews() // 布局变化时重新计算进度条位置 updateProgressFrame() } // 外部调用:传入AVPlayer更新加载进度 func updateLoadedProgress(with player: AVPlayer?) { guard let player = player, let duration = player.currentItem?.duration else { loadedProgressView.frame = .zero return } let durationSeconds = CMTimeGetSeconds(duration) if durationSeconds.isNaN || durationSeconds <= 0 { loadedProgressView.frame = .zero return } // 计算已加载的总时长 var loadedSeconds: TimeInterval = 0 for timeRange in player.currentItem?.loadedTimeRanges ?? [] { loadedSeconds += CMTimeGetSeconds(timeRange.timeRangeValue.duration) } // 计算进度比例并更新视图 let progressRatio = CGFloat(loadedSeconds / durationSeconds) updateProgressFrame(with: progressRatio) } // 内部方法:更新进度条Frame private func updateProgressFrame(with ratio: CGFloat = 0) { guard let trackRect = trackRect(forBounds: bounds) else { return } loadedProgressView.frame = CGRect( x: trackRect.origin.x, y: trackRect.origin.y, width: trackRect.width * ratio, height: trackRect.height ) } }
实际使用示例
在控制器中关联AVPlayer并监听加载状态:
import UIKit import AVFoundation class PlayerViewController: UIViewController { private let player = AVPlayer(url: URL(string: "你的音视频URL")!) private let progressSlider = AVPlayerProgressSlider() override func viewDidLoad() { super.viewDidLoad() // 初始化Slider基础样式(和你原有代码一致) progressSlider.tintColor = .blue progressSlider.maximumTrackTintColor = .gray progressSlider.minimumValue = 0 // 添加Slider到视图 view.addSubview(progressSlider) progressSlider.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ progressSlider.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20), progressSlider.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), progressSlider.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20), progressSlider.heightAnchor.constraint(equalToConstant: 20) ]) // 监听AVPlayer的加载范围变化,实时更新进度条 player.currentItem?.addObserver(self, forKeyPath: "loadedTimeRanges", options: .new, context: nil) // 开始播放 player.play() } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "loadedTimeRanges" { progressSlider.updateLoadedProgress(with: player) // 同时更新Slider的最大值(视频加载完成后获取准确时长) if let duration = player.currentItem?.duration { let durationSeconds = CMTimeGetSeconds(duration) if !durationSeconds.isNaN { progressSlider.maximumValue = Float(durationSeconds) } } } } deinit { // 移除监听,避免内存泄漏 player.currentItem?.removeObserver(self, forKeyPath: "loadedTimeRanges") } }
关键注意事项
- 监听
loadedTimeRanges可以实时获取AVPlayer的加载进度,每次加载范围变化都会触发回调。 - 在
layoutSubviews中更新进度条Frame,确保屏幕旋转或Slider布局变化时进度条位置正确。 - 进度条要放在Track层级下方,避免遮挡滑块交互。
- 处理视频未加载完成时的异常情况(比如duration为NaN),避免出现计算错误。
内容的提问来源于stack exchange,提问作者kesape
相关产品推荐
相关产品推荐

