You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 09:07:49