返回

AVPlayer带缓冲的进度条及全屏模式

IOS

项目工程:

import UIKit
import AVFoundation

class ViewController: UIViewController {

    private var player: AVPlayer!
    private var playerLayer: AVPlayerLayer!
    private var progressView: UIProgressView!
    private var playButton: UIButton!
    private var fullScreenButton: UIButton!
    private var timeLabel: UILabel!
    private var isFullScreen = false

    override func viewDidLoad() {
        super.viewDidLoad()
        setupPlayer()
        setupUI()
        configureGestures()
    }

    private func setupPlayer() {
        let videoURL = Bundle.main.url(forResource: "video", withExtension: "mp4")!
        player = AVPlayer(url: videoURL)
        playerLayer = AVPlayerLayer(player: player)
        playerLayer.frame = view.bounds
        view.layer.addSublayer(playerLayer)

        player.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: [], context: nil)
        player.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 2), queue: DispatchQueue.main, using: { [weak self] time in
            self?.updateProgressView(time: time)
        })
    }

    private func setupUI() {
        progressView = UIProgressView()
        progressView.progressViewStyle = .default
        progressView.tintColor = .red
        progressView.frame = CGRect(x: 0, y: view.bounds.height - 40, width: view.bounds.width, height: 2)
        view.addSubview(progressView)

        playButton = UIButton(type: .system)
        playButton.setImage(UIImage(systemName: "play.fill"), for: .normal)
        playButton.addTarget(self, action: #selector(playPause), for: .touchUpInside)
        playButton.frame = CGRect(x: (view.bounds.width - 50) / 2, y: view.bounds.height - 100, width: 50, height: 50)
        view.addSubview(playButton)

        fullScreenButton = UIButton(type: .system)
        fullScreenButton.setImage(UIImage(systemName: "arrowshape.turn.up.right"), for: .normal)
        fullScreenButton.addTarget(self, action: #selector(toggleFullScreen), for: .touchUpInside)
        fullScreenButton.frame = CGRect(x: view.bounds.width - 50, y: view.bounds.height - 100, width: 50, height: 50)
        view.addSubview(fullScreenButton)

        timeLabel = UILabel()
        timeLabel.text = "00:00/00:00"
        timeLabel.textAlignment = .center
        timeLabel.textColor = .white
        timeLabel.frame = CGRect(x: 0, y: view.bounds.height - 60, width: view.bounds.width, height: 20)
        view.addSubview(timeLabel)
    }

    private func configureGestures() {
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGesture))
        view.addGestureRecognizer(panGesture)
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "currentItem.loadedTimeRanges" {
            updateProgressView(time: player.currentTime())
        }
    }

    private func updateProgressView(time: CMTime) {
        if let loadedTimeRanges = player.currentItem?.loadedTimeRanges, !loadedTimeRanges.isEmpty {
            let timeRange = loadedTimeRanges[0]
            let start = CMTimeGetSeconds(timeRange.start)
            let end = CMTimeGetSeconds(timeRange.end)
            let loadedDuration = end - start
            progressView.progress = Float(loadedDuration / CMTimeGetSeconds(player.currentItem!.duration))
        }
    }

    @objc private func playPause() {
        if player.isPlaying {
            player.pause()
            playButton.setImage(UIImage(systemName: "play.fill"), for: .normal)
        } else {
            player.play()
            playButton.setImage(UIImage(systemName: "pause.fill"), for: .normal)
        }
    }

    @objc private func toggleFullScreen() {
        isFullScreen = !isFullScreen

        if isFullScreen {
            let screen = UIScreen.main
            let bounds = screen.bounds

            let frame = CGRect(x: bounds.origin.x, y: bounds.origin.y, width: bounds.width, height: bounds.height)
            playerLayer.frame = frame
            self.view.frame = frame
        } else {
            let frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height)
            playerLayer.frame = frame
            self.view.frame = frame
        }
    }

    @objc private func panGesture(gesture: UIPanGestureRecognizer) {
        let translation = gesture.translation(in: view)
        if translation.x > 0 {
            player.seek(to: player.currentTime() + CMTime(seconds: 5, preferredTimescale: 1))
        } else if translation.x < 0 {
            player.seek(to: player.currentTime() - CMTime(seconds: 5, preferredTimescale: 1))
        }
    }
}

项目效果图:
AVPlayer自定义支持全屏的播放器

教程:

  1. 导入AVFoundation框架。
  2. 创建AVPlayer对象并设置播放URL。
  3. 创建AVPlayerLayer对象并将其添加到视频播放视图中。
  4. 添加KVO观察者来更新进度条。
  5. 添加周期性时间观察者来更新当前播放时间和总时间。
  6. 创建进度条、播放/暂停按钮、全屏按钮和时间标签。
  7. 添加手势识别器来实现视频快进/快退。
  8. 实现全屏播放功能。

总结:

本文介绍了如何使用AVPlayer构建一个自定义的视频播放器,该播放器具有带缓冲显示的进度条、可调节播放进度的播放条,以及显示当前播放时间和总时间的标签。此外,还实现了全屏播放功能。