当我们探讨播放功能时,上一篇文章简要介绍了与核心类和API相关的内容,并提供了一个简单的播放案例。然而,实际使用视频播放器时,我们通常不会采用类似的写法,而是更倾向于构建一个完整、可重用的播放组件。在接下来的部分,让我们深入探讨创建播放组件的过程。
本篇博客我们先来实现一个简单的仅可播放的是播放组件,后续会不断地丰富它的功能。
首先我们创建一个可以用来显示视频内容的视图,创建一个名PHPlayerView的类。
import UIKit
import AVFoundation
class PHPlayerView: UIView {
/// 重写layerClass方法,
override class var layerClass: AnyClass{
get {
return AVPlayerLayer.self
}
}
/// 重写init方法
///
/// - Parameters:
/// - player: 播放器
init(player:AVPlayer) {
super.init(frame: CGRectZero)
guard let playerLayer = self.layer as? AVPlayerLayer else { return }
playerLayer.player = player
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
1.通过重写layerClass熟悉的get方法,使得AVPlayerLayer为PHPlayerView的支持图层。
2.自定义一个init方法将player与AVPlayerLayer图层进行关联。
视频控制器里面封装了我们创建的播放器组件的所有功能,也是我们处理系统核心播放API的地方。创建一个名为PHPlayerController的类。
import UIKit
import AVFoundation
let status_keypath = "status"
var playerItemContext = 0
class PHPlayerController: NSObject {
/// 资源
private var asset:AVAsset?
/// AVPlatyerItem
private var playerItem:AVPlayerItem?
/// AVPlayer
private var player:AVPlayer?
/// 播放视图
private var playerView:PHPlayerView?
/// 只读属性view返回PHPlayerView实例
var view:UIView? {
get {
return playerView
}
}
/// 重写init方法
///
/// - Parameters:
/// - url: 资源URL
init(url:URL) {
super.init()
asset = AVAsset(url: url)
prepareToPlay()
}
/// 准备播放
private func prepareToPlay() {
let keys = ["tracks","duration","commonMetadata"]
guard let asset = asset else { return }
playerItem = AVPlayerItem(asset: asset, automaticallyLoadedAssetKeys: keys)
guard let playerItem = playerItem else { return }
player = AVPlayer(playerItem: playerItem)
guard let player = player else { return }
playerView = PHPlayerView(player: player)
playerItem.addObserver(self, forKeyPath: status_keypath, context: &playerItemContext)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if context == &playerItemContext {
guard let playerItem = playerItem else { return }
guard let player = player else { return }
if playerItem.status == .readyToPlay{
playerItem.removeObserver(self, forKeyPath: status_keypath)
player.play()
}
} else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
}
1.在PHPlayerController中定义了一系列的视频播放使用的对象,还有视频播放视图PHPlayerView的实例,以及一个只读属性view返回播放视图。
2.自定义初始化方法传入资源的URL。
3.定义私有方法prepareToPlay创建播放器及播放视图,通过init(asset: AVAsset, automaticallyLoadedAssetKeys: [String]?)方法,让框架自动载入资源的tracks,duration,commonMetadata属性,使用KVO监听AVPlayerItem的status属性。
4.实现监听的方法,当属性由.unknown变为.readyToPlay的时候,palyer调用playe方法来进行视频播放。
一个带有视频播放功能的播放组件就已经构建完成了,下面我们就在视图控制器来开始使用它。
class ViewController: UIViewController {
/// 播放控制器
var playerController:PHPlayerController?
override func viewDidLoad() {
super.viewDidLoad()
guard let url = Bundle.main.url(forResource: "waves", withExtension: "mp4") else { return }
playerController = PHPlayerController(url: url)
guard let playerView = playerController?.view else { return }
playerView.frame = view.bounds
view.addSubview(playerView)
}
}
使用的代码并不多,但我们的确已经实现了一个可以播放的视频组件,下面我们会逐步的往组件中添加播放,暂停,快进等各种提升用户体验的功能。