swift 利用AVFoundation,SnapKit进行网络
2018-09-10 本文已影响77人
MrLSX
效果图:
QQ20180912-143551.gif
QQ20180912-143648.gif
1.创建视图需要的xib
image.png
2.创建视图内控件的对应属性
//进度条
@IBOutlet weak var slider: UISlider!
//视频视图
@IBOutlet weak var videoBox: UIView!
//视频视图的长宽
@IBOutlet weak var videoHeight: NSLayoutConstraint!
@IBOutlet weak var videoBoxWeight: NSLayoutConstraint!
//下边视图
@IBOutlet weak var bottonView: UIView!
//上边视图
@IBOutlet weak var topView: UIView!
//视频title
@IBOutlet weak var titleLabel: UILabel!
//时间进度
@IBOutlet weak var currentTimeLabel: UILabel!
@IBOutlet weak var totalTimeLabel: UILabel!
3.创建player对象
var player:AVPlayer?
var playeritem:AVPlayerItem?
var playerLayer:AVPlayerLayer!
4.初始化player对象以及设置player的一些属性
//测试用的MP4对象
let contentUrl = URL(fileURLWithPath: "本地路径.mp4")
let url = URL.init(string: "网络路径.mp4")!
playeritem = AVPlayerItem(url: url)
// 监听状态改变
playeritem?.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.new, context: nil)
// 将视频资源赋值给视频播放对象
player = AVPlayer(playerItem: playeritem)
// 初始化视频显示layer
playerLayer = AVPlayerLayer(player: player)
// 设置填充模式
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspect
//给playerLayer添加到videoBox的Layer上
videoBox.layer.addSublayer(playerLayer)
//设置playerLayer的frame
playerLayer.frame = videoBox.bounds
// 位置放在最底下
videoBox.layer.insertSublayer(playerLayer, at: 0)
5.播放视频资源
//如果资源正确自动播放,不正确打印“加载异常”
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "status"{
if playeritem?.status == AVPlayerItemStatus.readyToPlay{
self.player?.play()
}else{
print("加载异常")
}
}
}
现在就可以播放视频了
6.显示进读
var link:CADisplayLink!
//计时器 正常情况下每秒60帧
link = CADisplayLink(target: self, selector: #selector(updata))
link.add(to: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)
@objc func updata(){
//当前时间
let currentTime = CMTimeGetSeconds((self.player?.currentTime())!)
//总时间
let totalTime = TimeInterval((playeritem?.duration.value)!) / TimeInterval((playeritem?.duration.timescale)!)
totalTimeLabel.text = formatPlayTime(secounds: totalTime)
currentTimeLabel.text = formatPlayTime(secounds: currentTime)
}
//时间转换成秒
func formatPlayTime(secounds:TimeInterval)->String{
if secounds.isNaN{
return "00:00"
}
let Min = Int(secounds / 60)
let Sec = Int(secounds.truncatingRemainder(dividingBy: 60))
return String(format: "%02d:%02d", Min, Sec)
}
}
7.滑动进度条
//判断slider是否在移动,移动的时候不更新进度的值
var slidering = false
//给slider添加事件
//按下
slider.addTarget(self, action: #selector(sliderTouchDown( _:)), for: .touchDown)
//谈起
slider.addTarget(self, action: #selector(sliderTouchUpOut( _:)), for: .touchUpInside)
slider.addTarget(self, action: #selector(sliderTouchUpOut( _:)), for: .touchUpOutside)
slider.addTarget(self, action: #selector(sliderTouchUpOut( _:)), for: .touchCancel)
@objc func sliderTouchUpOut(_ slider:UISlider){
//当播放正常的时候才能滑动slider
if player?.status == AVPlayerStatus.readyToPlay{
let duration = slider.value * Float(TimeInterval((playeritem?.duration.value)!) / TimeInterval((playeritem?.duration.timescale)!))
let seekTime = CMTimeMake(Int64(duration), 1)
player?.seek(to: seekTime, completionHandler: { (_) in
self.slidering = false
})
}
}
@objc func sliderTouchDown(_ slider:UISlider){
slidering = true
}
在updata()内添加
//slider滑动时不更新slider
if !slidering{
slider.value = Float(currentTime/totalTime)
}
8.button事件
//返回键
@IBAction func back(_ sender: UIButton) {
if !isFullScreen {
self.navigationController?.popViewController(animated: true)
}else{
//强制竖屏并且重新设置视图的Frame以及playerlayer的Frame
isFullScreen = false
UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
videoBox.snp.removeConstraints()
videoBox.snp.makeConstraints { (make) in
make.top.equalTo(44)
make.left.right.equalToSuperview()
videoBox.frame.size = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.width * 9 / 16)
}
playerLayer.frame = videoBox.bounds
}
}
//播放暂停
@IBOutlet weak var playButton: UIButton!
@IBAction func play(_ sender: UIButton) {
if !sender.isSelected {
player?.pause()
}else{
if player?.status == AVPlayerStatus.readyToPlay{
player?.play()
}
}
sender.isSelected = !sender.isSelected
}
//全屏
var isFullScreen = false
var isleft = false
@IBAction func fullScreen(_ sender: UIButton) {
if !isFullScreen{
//强制横屏
UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation")
videoBox.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
playerLayer.frame = UIScreen.main.bounds
isFullScreen = true
}
else{
//强制横屏以及判断是否是横屏后再次点击横屏键
if isleft{
UIDevice.current.setValue(UIInterfaceOrientation.landscapeRight.rawValue, forKey: "orientation")
isleft = false
}else{
UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation")
isleft = true
}
}
}
9.双击暂停播放,单击隐藏上下视图
//给videoBox添加两个一个单击,一个双击事件
@IBOutlet var singleTouch: UITapGestureRecognizer!
@IBAction func singleTouch(_ sender: UITapGestureRecognizer) {
topView.isHidden = !topView.isHidden
bottonView.isHidden = !bottonView.isHidden
}
//双击
@IBOutlet var doubleTouch: UITapGestureRecognizer!
@IBAction func doubleTouch(_ sender: Any) {
//给playButton发送一次点击信息,相当于点击一次BUtton
playButton.sendActions(for: .touchUpInside)
}
10.其他
//因为videoBox添加了单击双击事件,所以要在viewDidLoad()添加一行代码,从而让双击事件触发
//单击事件必须在双击事件检测失败后才执行,这样单击事件会有点延迟
singleTouch.require(toFail: doubleTouch)
//移除监听事件
playeritem?.removeObserver(self, forKeyPath: "status")
项目git地址:https://github.com/Liushaungxi/AVPlayer
参考文档:https://www.jianshu.com/p/d35980045c2b