swift4.0實(shí)現(xiàn)視頻播放、屏幕旋轉(zhuǎn)、倍速播放、手勢(shì)調(diào)節(jié)及鎖屏面板等功能實(shí)例
前言
學(xué)習(xí)swift有段時(shí)間了,原來(lái)寫(xiě)過(guò)一個(gè)基于 swift 3.0 的視頻播放,后來(lái)有同學(xué)聯(lián)系我說(shuō),在音頻鎖屏的情況下,無(wú)法用控制面板拖動(dòng)進(jìn)度條調(diào)節(jié)播放進(jìn)度,所以又將原來(lái)的代碼拿過(guò)來(lái)重新整理了下也順便更新到了4.0版本。在把原來(lái)的代碼拿來(lái)的時(shí)候發(fā)現(xiàn)原來(lái)有好多地方都是錯(cuò)誤的,原來(lái)在 OC 項(xiàng)目里面已經(jīng)寫(xiě)過(guò)一遍關(guān)于視頻播放的東西所以就按照原來(lái)的邏輯寫(xiě)了 swift 版本,其實(shí)里面很多代碼我也是通過(guò)查找資料和看文檔拼湊出來(lái)的,對(duì)于 swift 的語(yǔ)句也是一知半解,希望各位看官多多包涵。
先來(lái)看一下實(shí)現(xiàn)的效果,一圖勝千言(第一張是 iOS 10系統(tǒng),第二張是 iOS 11系統(tǒng))。
demo下載地址 (本地下載)
工程介紹
簡(jiǎn)單說(shuō)一下工程結(jié)構(gòu),所有關(guān)于布局都是在Player文件夾下的MPlayerViewModel文件中,考慮到耦合度的原因,所以將視頻播放的所有 UI 布局全部抽離出來(lái),在播放器 view 里將會(huì)頻繁看到一個(gè)叫viewModel的對(duì)象,它既 UI 布局也是布局控件的所有者。視頻播放的布局是基于SnapKit三方庫(kù)來(lái)布局了,因?yàn)樵贠C里用慣了Masonry所以工程里依然沿用這個(gè)庫(kù)。主要代碼是放到MPlayerView這個(gè)文件中的,其中還有一個(gè)由 OC 寫(xiě)的DeviceTool文件主要用來(lái)做頁(yè)面強(qiáng)制旋轉(zhuǎn)用的,強(qiáng)制旋轉(zhuǎn)這一部分我現(xiàn)在還沒(méi)有更好的解決辦法只能橋接 OC 里的方法。
初始化播放器方法
視頻播放界面我用的是一個(gè)單例實(shí)現(xiàn)的,剛開(kāi)始不是用單例實(shí)現(xiàn),但是為了把代碼拆出來(lái)放到各自的功能區(qū)所以用單例實(shí)現(xiàn)是最好的方法。由于swift放棄了OC里的dispatch_once實(shí)現(xiàn)單例方法,swift3.0以后的單例寫(xiě)法:
/// 創(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之后重寫(xiě)init方法必須實(shí)現(xiàn)required init方法,這么做也是為了安全,因?yàn)樵贠C里init方法并不能保證子類完成初始化,增加required“這是由初始化方法的完備性需求所決定的,以保證類型的安全。在創(chuàng)建視頻播放視圖有兩種創(chuàng)建方式:1.用單利創(chuàng)建。2.init 初始化 ,這兩種方法都可以達(dá)到視頻播放的效果。
1.單利初始化
self.playerView = MPlayerView.shared.initWithFrame(frame: self.view.frame, videoUrl: videoUrl, type: "VIDEO")
2.init 初始化
self.playerView = MPlayerView().initWithFrame(frame: CGRect.init(x: 0, y: 0, width: Screen_width, height: Screen_width * 9/16), videoUrl: videoUrl, type: "VIDEO")
手勢(shì)滑動(dòng)及注意事項(xiàng)
由于swift里面有嚴(yán)格的類型檢查,就比如在做手勢(shì)滑動(dòng)的時(shí)候,手勢(shì)剛開(kāi)始滑動(dòng)的時(shí)候肯定需要記錄一下當(dāng)前播放器的位置我在項(xiàng)目中是定義的sumTime屬性是一個(gè)CMTime類型,如果在OC里大可不必這樣,來(lái)看一下swift與OC代碼的區(qū)別
swift寫(xiě)法
/// 給sumTime初值 let time = self.player?.currentTime() self.sumTime = CMTimeMake((time?.value)!, (time?.timescale)!)
OC寫(xiě)法
// 給sumTime初值 CMTime time = self.player.currentTime; self.sumTime = time.value/time.timescale;
滑動(dòng)的距離是一個(gè)Double類型,而self.sumTime是CMTime類型,倆者肯定不能想加算出結(jié)束滑動(dòng)的距離,所以將double類型轉(zhuǎn)換成CMTime類型用以下方法:
CMTime.init(seconds: Double.init(value/200), preferredTimescale: CMTimeScale(NSEC_PER_SEC))
如果是OC的話直接括號(hào)強(qiáng)轉(zhuǎn)類型即可實(shí)現(xiàn)。
知道滑動(dòng)的距離和記錄滑動(dòng)前的距離倆者想加即是當(dāng)前位置,轉(zhuǎn)化成CMTime類型:
self.sumTime = CMTimeAdd(self.sumTime!, addend)
手勢(shì)是滑動(dòng)了,但是進(jìn)度條也是要跟著一起滑動(dòng)的,有人說(shuō)我把進(jìn)度條刷新放到player的代理里面,手勢(shì)滑動(dòng)完只需要把時(shí)間傳給播放器,播放器根據(jù)當(dāng)前時(shí)間和總時(shí)間去更新進(jìn)度條,這樣做也對(duì),但是有一點(diǎn)就是,如果網(wǎng)速不好,手勢(shì)已經(jīng)滑動(dòng)到5分鐘了,而進(jìn)度條還停留在1分鐘的地方,播放器緩存完畢了,進(jìn)度條會(huì)瞬間跳到5分鐘,從而造成卡頓的假象體驗(yàn)也不是很好,所以解決這個(gè)方法是手勢(shì)滑動(dòng)的時(shí)候也更新進(jìn)度條,但是手勢(shì)滑動(dòng)的時(shí)候都是CMTime類型,怎么轉(zhuǎn)成Float類型,因?yàn)閟lider?.value是float類型??梢赃@樣:通過(guò)CMTimeGetSeconds方法得到一個(gè)Float64再通過(guò)Float.init方法得到一個(gè)float類型,看一下實(shí)現(xiàn):
let sliderTime = CMTimeGetSeconds(self.sumTime!)/CMTimeGetSeconds(totalMovieDuration) self.slider?.value = Float.init(sliderTime)
想查看整個(gè)過(guò)程可以看播放器手勢(shì)添加與創(chuàng)建這一塊,我已經(jīng)用MARK:標(biāo)記起來(lái)了。
設(shè)置控制面板信息
在視頻播放過(guò)程中,對(duì)視頻的監(jiān)聽(tīng)是必不可少的,監(jiān)聽(tīng)播放器狀態(tài),播放器緩存...等,由于播放器比較簡(jiǎn)單,功能較少,剛開(kāi)始我只監(jiān)聽(tīng)了status屬性,后來(lái)我加上來(lái)loadedTimeRanges緩存狀態(tài),緩存這部分的緩存進(jìn)度計(jì)算我已經(jīng)實(shí)現(xiàn)了,但是沒(méi)有用到只是簡(jiǎn)單的打印了一下。
在對(duì)播放器status屬性監(jiān)聽(tīng)中加入了控制面板信息,是由MPNowPlayingInfoCenter來(lái)實(shí)現(xiàn)的,通過(guò)改變nowPlayingInfo里面對(duì)應(yīng)的信息來(lái)更新面板信息,里面有好多屬性,比如MPMediaItemPropertyTitle設(shè)置音頻標(biāo)題,MPMediaItemPropertyArtist作者、MPNowPlayingInfoPropertyElapsedPlaybackTime當(dāng)前播放過(guò)的時(shí)間、MPMediaItemPropertyPlaybackDuration播放總時(shí)間等等。剛開(kāi)始做的時(shí)候因?yàn)殒i屏要更新時(shí)間,而nowPlayingInfo又是一個(gè)字典類型的再加上需要更新界面布局的時(shí)間和進(jìn)度條,直接將播放器時(shí)間強(qiáng)制轉(zhuǎn)換成 string 類型,所以將這一部分放到了時(shí)間觀察里面,因?yàn)闀r(shí)間觀察會(huì)一直進(jìn)行所以鎖屏界面信息也會(huì)一直更新,這樣帶來(lái)一個(gè)問(wèn)題就是鎖屏界面的圖片如果是網(wǎng)絡(luò)圖片,每1秒就要請(qǐng)求一下圖片而且要不斷的更新這樣帶來(lái)的結(jié)果可想而知。后來(lái)才知道,將MPNowPlayingInfoPropertyElapsedPlaybackTime屬性設(shè)置成self.player!.currentTime()播放器當(dāng)前時(shí)間就會(huì)自動(dòng)更新控制面板信息,調(diào)用的地方也很關(guān)鍵,必須放在播放器已經(jīng)播放的監(jiān)聽(tīng)里面。
配置遠(yuǎn)程控制顯示的信息
響應(yīng)遠(yuǎn)程控制是由MPRemoteCommandCenter來(lái)實(shí)現(xiàn)的,里面有很多屬性,比如:playCommand播放響應(yīng)事件、pauseCommand 暫停響應(yīng)事件、nextTrackCommand下一曲響應(yīng)事件、likeCommand喜歡按鈕,類似網(wǎng)易云音樂(lè)的那個(gè)鎖屏,如果設(shè)置了likeCommand則dislikeCommand是上一首響應(yīng)事件、previousTrackCommand上一首,外部拖動(dòng)進(jìn)度條是changePlaybackPositionCommand,系統(tǒng)有一個(gè)專門(mén)的方法來(lái)出來(lái)遠(yuǎn)程拖動(dòng)進(jìn)度條響應(yīng)事件:
open func addTarget(handler: @escaping (MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus) -> Any
大概控制面板能用到的這些信息差不多也就這么多,如果想了解更多的可以看一下文檔或者查閱資料。
屏幕旋轉(zhuǎn)問(wèn)題
一個(gè)視頻播放實(shí)現(xiàn)起來(lái)并不困難,只要處理好player與platitem就行了。最難的就是,如果手機(jī)屏幕旋轉(zhuǎn),怎么能讓視頻跟著屏幕自適應(yīng)呢,我在工程里面通過(guò)UIDevice變化添加的是屏幕旋轉(zhuǎn)監(jiān)聽(tīng):
/**
* 監(jiān)聽(tīng)設(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)掉,就是控制中心那個(gè)開(kāi)關(guān),用戶旋轉(zhuǎn)屏幕,怎么能讓畫(huà)面跟著跑呢,我百度的很多資料,試了也很多方法,但是都不理想,用的還是OC的代碼,因?yàn)閟wift里面移除了NSInvocation屬性,用的依然是OC的屏幕強(qiáng)制旋轉(zhuǎn),只能使用橋接文件:
//這個(gè)方法是在網(wǎng)上找的
+ (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開(kāi)始是因?yàn)? 1 兩個(gè)參數(shù)已經(jīng)被selector和target占用
[invocation setArgument:&val atIndex:2];
[invocation invoke];
}
}
因?yàn)樽龅氖且曨l播放,所以進(jìn)入后臺(tái)后視頻會(huì)暫停,這個(gè)屬于正?,F(xiàn)象,如果在視頻模式下,進(jìn)入后臺(tái)利用控制面板是無(wú)法將視頻播放的,如果在音頻模式下,進(jìn)入后臺(tái)利用控制面板是可以讓視頻播放的。大概就介紹這么多,一言半句也說(shuō)得不是很明白,如果還有不明白的知識(shí)點(diǎn)可以去demo中自己去查,我也是一個(gè)初學(xué)者里面很多東西都是查資料得來(lái)的并不能保證其內(nèi)容的正確性。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)我們的支持。
上一篇:Swift如何調(diào)用Objective-C的可變參數(shù)函數(shù)詳解
欄 目:Swift
下一篇:swift中利用runtime交換方法的實(shí)現(xiàn)示例
本文標(biāo)題:swift4.0實(shí)現(xiàn)視頻播放、屏幕旋轉(zhuǎn)、倍速播放、手勢(shì)調(diào)節(jié)及鎖屏面板等功能實(shí)例
本文地址:http://www.jygsgssxh.com/a1/Swift/11951.html
您可能感興趣的文章
- 01-11Swift利用純代碼實(shí)現(xiàn)時(shí)鐘效果實(shí)例代碼
- 01-11Swift實(shí)現(xiàn)監(jiān)聽(tīng)鍵盤(pán)通知及一些處理詳解
- 01-11swift中利用runtime交換方法的實(shí)現(xiàn)示例
- 01-11swift4 使用DrawerController實(shí)現(xiàn)側(cè)滑菜單功能的示例代碼
- 01-11Swift實(shí)現(xiàn)“或”操作符的3種方法示例
- 01-11Swift仿選擇電影票的效果并實(shí)現(xiàn)無(wú)限/自動(dòng)輪播的方法
- 01-11Swift4.1轉(zhuǎn)場(chǎng)動(dòng)畫(huà)實(shí)現(xiàn)側(cè)滑抽屜效果
- 01-11Swift使用CollectionView實(shí)現(xiàn)廣告欄滑動(dòng)效果
- 01-11RxSwift實(shí)現(xiàn)替換delegate的方法示例


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-11Swift利用Decodable解析JSON的一個(gè)小問(wèn)題
- 01-11swift中defer幾個(gè)簡(jiǎn)單的使用場(chǎng)景詳解
- 01-11Swift中初始化init的方法小結(jié)
- 01-11Swift中defer關(guān)鍵字推遲執(zhí)行示例詳解
- 01-11Swift利用純代碼實(shí)現(xiàn)時(shí)鐘效果實(shí)例代碼
- 01-11Swift中定義單例的方法實(shí)例
- 01-11Swift中排序算法的簡(jiǎn)單取舍詳解
- 01-11Swift Json實(shí)例詳細(xì)解析
- 01-11Swift如何為設(shè)置中心添加常用功能
- 01-11Swift利用指紋識(shí)別或面部識(shí)別為應(yīng)用添
隨機(jī)閱讀
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 01-10C#中split用法實(shí)例總結(jié)
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 04-02jquery與jsp,用jquery
- 01-10delphi制作wav文件的方法
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文


