最近在學(xué)習(xí)swift,恰巧現(xiàn)在負責(zé)的項目中有關(guān)于視頻播放的一些東西,就想著用swift去實現(xiàn),視頻播放在原OC項目中已實現(xiàn)基本功能,所以就模仿OC去寫swift視頻播放,在寫的過程中發(fā)現(xiàn)與OC的實現(xiàn)方法出入還是比較大的,網(wǎng)上關(guān)于swift視頻播放資料不是很全面,所以想給大家分享一下知識點情萤。
先來看一下實現(xiàn)的效果鸭蛙,沒實現(xiàn)效果我知道同學(xué)肯定看不下去
簡單說一下工程結(jié)構(gòu),所有關(guān)于播放的東西以及布局都是在AVPlayer
文件夾下筋岛,視頻播放的布局是基于SnapKit
三方庫來布局了娶视,因為在OC里用慣了Masonry
所以工程里依然沿用這個庫。因為項目里面有線路切換和音視頻切換功能睁宰,肪获,如果你有未加密的視頻鏈接或者音頻鏈接直接把sdk刪掉也是可以的,也是可以正常播放的柒傻。
關(guān)鍵代碼是放在MPlayerView
這個文件中孝赫,輔助視圖布局的三個文件分別是:RateView
音視頻倍速切換ResolutionView
高清度切換SwitchCircuitView
線路切換。
剛開始做的時候红符,把所有的功能代碼都全部放入MPlayerView
這個文件中青柄,發(fā)現(xiàn)耦合度太多,代碼可讀性差预侯,所以我將代碼拆出來各自負責(zé)的模塊放入各自的功能致开,比如高清度切換功能,實現(xiàn)的功能就是放在ResolutionView
文件下萎馅。
在這里就不貼太多的代碼双戳,我將在本文末放demo的下載地址。現(xiàn)在就簡單聊一下實現(xiàn)過程吧糜芳。視頻播放界面我用的是一個單例實現(xiàn)的飒货,剛開始不是用單例實現(xiàn)魄衅,但是為了把代碼拆出來放到各自的功能區(qū)所以用單例實現(xiàn)是最好的方法。由于swift放棄了OC里的dispatch_once
實現(xiàn)單例方法塘辅,swift3.0以后的單例寫法:
/// 創(chuàng)建播放器單例
static let shared = MPlayerView()
private override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
在swift3.0之后重寫init方法必須實現(xiàn)required init
方法晃虫,這么做也是為了安全,因為在OC里init
方法并不能保證子類完成初始化莫辨,增加required
“這是由初始化方法的完備性需求所決定的傲茄,以保證類型的安全。
由于swift里面有嚴(yán)格的類型檢查沮榜,就比如在做手勢滑動的時候盘榨,手勢剛開始滑動的時候肯定需要記錄一下當(dāng)前播放器的位置我在項目中是定義的sumTime
屬性是一個CMTime
類型,如果在OC里大可不必這樣蟆融,來看一下swift與OC代碼的區(qū)別
swift寫法
/// 給sumTime初值
let time = self.player?.currentTime()
self.sumTime = CMTimeMake((time?.value)!, (time?.timescale)!)
OC寫法
// 給sumTime初值
CMTime time = self.player.currentTime;
self.sumTime = time.value/time.timescale;
滑動的距離是一個Double
類型草巡,而self.sumTime
是CMTime類型,倆者肯定不能想加算出結(jié)束滑動的距離型酥,所以將double類型轉(zhuǎn)換成CMTime類型用以下方法:
CMTime.init(seconds: Double.init(value/200), preferredTimescale: CMTimeScale(NSEC_PER_SEC))
如果是OC的話直接括號強轉(zhuǎn)類型即可實現(xiàn)山憨。
知道滑動的距離和記錄滑動前的距離倆者想加即是當(dāng)前位置,轉(zhuǎn)化成CMTime類型:
self.sumTime = CMTimeAdd(self.sumTime!, addend)
手勢是滑動了弥喉,但是進度條也是要跟著一起滑動的郁竟,有人說我把進度條刷新放到player的代理里面,手勢滑動完只需要把時間傳給播放器由境,播放器根據(jù)當(dāng)前時間和總時間去更新進度條棚亩,這樣做也對,但是有一點就是虏杰,如果網(wǎng)速不好讥蟆,手勢已經(jīng)滑動到5分鐘了,而進度條還停留在1分鐘的地方纺阔,播放器緩存完畢了瘸彤,進度條會瞬間跳到5分鐘,從而造成卡頓的假象體驗也不是很好笛钝,所以解決這個方法是手勢滑動的時候也更新進度條质况,但是手勢滑動的時候都是CMTime類型,怎么轉(zhuǎn)成Float
類型玻靡,因為slider?.value
是float類型结榄。可以這樣:通過CMTimeGetSeconds
方法得到一個Float64
再通過Float.init
方法得到一個float類型啃奴,看一下實現(xiàn):
let sliderTime = CMTimeGetSeconds(self.sumTime!)/CMTimeGetSeconds(totalMovieDuration)
self.slider?.value = Float.init(sliderTime)
想查看整個過程可以看播放器手勢添加與創(chuàng)建
這一塊,我已經(jīng)用MARK:
標(biāo)記起來了雄妥。
一個視頻播放實現(xiàn)起來并不困難最蕾,只要處理好player
與platitem
就行了依溯。最難得就是,如果手機屏幕旋轉(zhuǎn)瘟则,怎么能讓視頻跟著屏幕自適應(yīng)呢黎炉,我在工程里面通過UIDevice
變化添加的是屏幕旋轉(zhuǎn)監(jiān)聽:
/**
* 監(jiān)聽設(shè)備旋轉(zhuǎn)通知
*/
private func listeningRotating() {
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
NotificationCenter.default.addObserver(self, selector: #selector(onDeviceOrientationChange), name:NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}
如果用戶把屏幕旋轉(zhuǎn)關(guān)掉,就是控制中心那個開關(guān)醋拧,用戶旋轉(zhuǎn)屏幕慷嗜,怎么能讓畫面跟著跑呢,我百度的很多資料丹壕,試了也很多方法庆械,但是都不理想,用的還是OC的代碼菌赖,因為swift里面移除了NSInvocation
屬性缭乘,用的依然是OC的屏幕強制旋轉(zhuǎn),只能使用橋接文件:
+ (void)interfaceOrientation:(UIInterfaceOrientation)orientation{
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
SEL selector = NSSelectorFromString(@"setOrientation:");
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
[invocation setSelector:selector];
[invocation setTarget:[UIDevice currentDevice]];
int val = orientation;
// 從2開始是因為0 1 兩個參數(shù)已經(jīng)被selector和target占用
[invocation setArgument:&val atIndex:2];
[invocation invoke];
}
}
大概就介紹這么多吧,主要就是記錄一下自己的學(xué)習(xí)過程,如果還有不明白的知識點可以去demo中自己去查.