iOS中視頻播放器的簡(jiǎn)單封裝詳解
前言
如果僅僅是播放視頻兩者的使用都非常簡(jiǎn)單,但是相比MediaPlayer,AVPlayer對(duì)于視頻播放的可控制性更強(qiáng)一些,可以通過自定義的一些控件來實(shí)現(xiàn)視頻的播放暫停等等。因此這里使用AVPlayer的視頻播放。
視頻播放器布局
首先使用xib創(chuàng)建CLAVPlayerView繼承UIView用來承載播放器,這樣我們?cè)谕獠渴褂玫臅r(shí)候,直接在控制器View或者Cell上添加CLAVPlayerView即可,至于播放器播放或者暫停等操作交給CLAVPlayerView來管理。下面來看一下CLAVPlayerView的結(jié)構(gòu)。
CLAVPlayerView的結(jié)構(gòu)
CLAVPlayerView的布局很簡(jiǎn)單,重點(diǎn)在于約束的添加和控件層次關(guān)系,添加約束只要自己挨個(gè)細(xì)心添加就沒有問題,需要注意控件的層次關(guān)系,從上圖中可以看出四個(gè)控件是分先后順序平行添加在CLAVPlayerView上的,要注意他們的層次關(guān)系,避免相互遮擋。
視頻播放器實(shí)現(xiàn)
布局完成之后,就是實(shí)現(xiàn)播放器功能,我們把播放器功能大致分為四部分來完成
一. 通過播放按鈕實(shí)現(xiàn)視頻播放。
首先CLAVPlayerView加載時(shí)需要將播放器layer添加到imageView的layer上,此時(shí)蒙版和底部工具條一定都是隱藏的,點(diǎn)擊中間播放按鈕,視頻開始播放并隱藏播放按鈕。因此我們需要在CLAVPlayerView的awakeFromNib方法中,在加載CLAVPlayerView時(shí)對(duì)其做一些處理。
1、初始化AVPlayer和AVPlayerLayer,并將AVPlayerLayer添加到imageView的layer上,在layoutSubviews中設(shè)置playerLayer的frame
// 初始化player 和playerLayer self.player = [[AVPlayer alloc]init]; self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; // imageView上添加playerLayer [self.imageView.layer addSublayer:self.playerLayer];
-(void)layoutSubviews { [super layoutSubviews]; self.playerLayer.frame = self.imageView.bounds; }
2、根據(jù)播放視頻的url創(chuàng)建AVPlayerItem
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"]; self.playerItem = [AVPlayerItem playerItemWithURL:url];
3、設(shè)置Slider原點(diǎn)以及最大點(diǎn)最小點(diǎn)圖片
// 設(shè)置Slider [self.progressSlider setThumbImage:[UIImage imageNamed:@"thumbImage"] forState:UIControlStateNormal]; [self.progressSlider setMaximumTrackImage:[UIImage imageNamed:@"MaximumTrackImage"] forState:UIControlStateNormal]; [self.progressSlider setMinimumTrackImage:[UIImage imageNamed:@"MinimumTrackImage"] forState:UIControlStateNormal];
4、給imageView添加tap手勢(shì),點(diǎn)擊imageView則顯示工具欄
//imageView添加手勢(shì) UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapAction:)]; [self.imageView addGestureRecognizer:tap];
注意:如果使用xib給imageVIew添加手勢(shì),則通過loadNibNamed加載xib的時(shí)候需要獲取返回?cái)?shù)組的firstObject,得到的才是xib的View,如果獲取lastObject,得到是的tap手勢(shì),會(huì)報(bào)錯(cuò)tap手勢(shì)對(duì)象沒有View的方法。
5、其他控件顯示以及狀態(tài)的設(shè)置
// 隱藏遮蓋版 self.coverView.hidden = YES; // 設(shè)置工具欄狀態(tài) self.toolView.alpha = 0; self.isShowToolView = NO; // 設(shè)置工具欄播放按鈕狀態(tài) self.playOrPauseBtn.selected = NO;
這蓋板只有播放完畢之后顯現(xiàn),點(diǎn)擊重播之后又隱藏,因此使用hidden直接隱藏即可,而工具欄需要重復(fù)顯示,并且我們?yōu)榱四茏尮ぞ邫诘娘@示有動(dòng)畫效果,這里通過設(shè)置toolView的alpha來顯示或隱藏工具欄,并通過isShowToolView來記錄toolView的顯示或隱藏。
6、中間播放按鈕的點(diǎn)擊
- (IBAction)playOrPauseBigBtnClick:(UIButton *)sender { // 隱藏中間播放按鈕,工具欄播放按鈕為選中狀態(tài) sender.hidden = YES; self.playOrPauseBtn.selected = YES; // 替換播放內(nèi)容 [self.player replaceCurrentItemWithPlayerItem:self.playerItem]; [self.player play]; [self addProgressTimer]; }
此時(shí),當(dāng)我們點(diǎn)擊中間播放按鈕播放器就可以播放視頻了。
二. 工具條的顯示與隱藏
在播放狀態(tài)時(shí),當(dāng)點(diǎn)擊imageView,就會(huì)彈出底部工具條,可以查看當(dāng)前播放的時(shí)間,視頻總時(shí)間或進(jìn)行暫停視頻、全屏播放等操作。如果沒有操作,工具欄會(huì)在5秒之后自動(dòng)隱藏。而當(dāng)未播放狀態(tài)時(shí),點(diǎn)擊imageView和中間播放按鈕效果一樣,開始播放視頻。
1、添加定時(shí)器,5秒鐘之后隱藏底部工具條,并提供移除定時(shí)器的方法。
/** toolView顯示時(shí)開始計(jì)時(shí),5s后隱藏toolView */ -(void)addShowTime { self.showTime = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(upDateToolView) userInfo:nil repeats:NO]; [[NSRunLoop mainRunLoop]addTimer:self.showTime forMode:NSRunLoopCommonModes]; } /** 將toolView隱藏 */ -(void)upDateToolView { self.isShowToolView = !self.isShowToolView; [UIView animateWithDuration:0.5 animations:^{ self.toolView.alpha = 0; }]; } /**移除定時(shí)器*/ -(void)removeShowTime { [self.showTime invalidate]; self.showTime = nil; }
2、imageView的tap手勢(shì)點(diǎn)擊方法實(shí)現(xiàn),這里分為幾種情況,當(dāng)視頻未播放的時(shí)候,點(diǎn)擊imageView不會(huì)顯示工具欄,而是與點(diǎn)擊中間播放按鈕相同,開始播放視頻,播放過程中點(diǎn)擊imageView會(huì)顯示工具欄,而如果此時(shí)點(diǎn)擊了工具欄中的暫停按鈕,播放暫停,則此時(shí)工具欄不會(huì)消失,重新開始播放視頻,工具欄在5秒內(nèi)消失。
/** imageView的tap手勢(shì)方法 */ -(void)tapAction:(UITapGestureRecognizer *)tap { // 當(dāng)未播放狀態(tài),點(diǎn)擊imageView等同于點(diǎn)擊中間播放按鈕,開始播放視頻 if (self.player.status == AVPlayerStatusUnknown) { [self playOrPauseBigBtnClick:self.playOrPauseBigBtn]; return; } // 記錄底部工具欄顯示或隱藏的狀態(tài) self.isShowToolView = !self.isShowToolView; // 如果需要工具欄顯示,添加動(dòng)畫顯示 if (self.isShowToolView){ [UIView animateWithDuration:0.5 animations:^{ self.toolView.alpha = 1; }]; // 工具欄的播放按鈕為播放狀態(tài)的時(shí)候,添加計(jì)時(shí)器,5秒鐘之后工具欄隱藏 if (self.playOrPauseBtn.selected) { [self addShowTime]; } // 如果需要隱藏工具欄,移除計(jì)時(shí)器,并將工具欄隱藏 }else{ [self removeShowTime]; [UIView animateWithDuration:0.5 animations:^{ self.toolView.alpha = 0; }]; } }
3、工具欄中播放/暫停按鈕的點(diǎn)擊也需要做一些處理,當(dāng)處于暫停狀態(tài)時(shí),工具欄alpha值設(shè)為1,并將定時(shí)器移除,重新開始播放視頻時(shí),則重新添加定時(shí)器開始計(jì)時(shí),5秒鐘之后讓工具欄消失。具體代碼會(huì)在播放時(shí)間、Slider與視頻播放的同步中詳細(xì)貼出。
三. 播放時(shí)間、Slider與視頻播放的同步
底部工具條中播放時(shí)間、視頻總時(shí)間以及Slider的滑動(dòng)需要與視頻播放時(shí)間進(jìn)行同步。
1、添加視頻播放和Slider的定時(shí)器,每1秒鐘重復(fù)調(diào)用更新時(shí)間label和Slider滑塊
/** slider定時(shí)器添加 */ -(void)addProgressTimer { self.progressTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateProgressInfo) userInfo:nil repeats:YES]; [[NSRunLoop mainRunLoop]addTimer:self.progressTimer forMode:NSRunLoopCommonModes]; } /** 移除slider定時(shí)器 */ -(void)removeProgressTimer { [self.progressTimer invalidate]; self.progressTimer = nil; } /** 更新slider和timeLabel */ - (void)updateProgressInfo { NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentTime); NSTimeInterval durationTime = CMTimeGetSeconds(self.player.currentItem.duration); self.timeLabel.text = [self timeToStringWithTimeInterval:currentTime]; self.allTimeLabel.text = [self timeToStringWithTimeInterval:durationTime]; self.progressSlider.value = CMTimeGetSeconds(self.player.currentTime) / CMTimeGetSeconds(self.player.currentItem.duration); if (self.progressSlider.value == 1) { [self removeProgressTimer]; self.coverView.hidden = NO; } }
獲取到的當(dāng)前播放時(shí)間和總時(shí)間是CMTime類型的,需要將他們轉(zhuǎn)化為NSTimeInterval并將秒轉(zhuǎn)化為分鐘和時(shí)間,將轉(zhuǎn)化方法提出來
/** 轉(zhuǎn)換播放時(shí)間和總時(shí)間的方法 */ -(NSString *)timeToStringWithTimeInterval:(NSTimeInterval)interval; { NSInteger Min = interval / 60; NSInteger Sec = (NSInteger)interval % 60; NSString *intervalString = [NSString stringWithFormat:@"%02ld:%02ld",Min,Sec]; return intervalString; }
2、當(dāng)點(diǎn)擊中間播放按鈕開始播放的時(shí)候添加定時(shí)器,同步更新播放時(shí)間和Slider,當(dāng)播放途中點(diǎn)擊工具欄暫停按鈕暫停播放,需要將視頻暫停,并移除定時(shí)器,重新開始播放時(shí)在添加定時(shí)器,并開始播放
/** toolView上暫停按鈕的點(diǎn)擊事件 */ - (IBAction)playOrPauseBtnClick:(UIButton *)sender { // 播放狀態(tài)按鈕selected為YES,暫停狀態(tài)selected為NO。 sender.selected = !sender.selected; if (!sender.selected) { self.toolView.alpha = 1; [self removeShowTime]; [self.player pause]; [self removeProgressTimer]; }else{ [self addShowTime]; [self.player play]; [self addProgressTimer]; } }
3、Slider的拖動(dòng)跳躍播放視頻
根據(jù)Slider滑動(dòng)拖動(dòng)滑動(dòng)位置播放視頻需要監(jiān)聽Slider的按下,拖動(dòng)(數(shù)據(jù)改變),松開三個(gè)階段。按下時(shí)移除定時(shí)器,拖動(dòng)時(shí)根據(jù)拖動(dòng)的值即時(shí)的計(jì)算當(dāng)前播放時(shí)間并顯示在label上,松開時(shí)計(jì)算當(dāng)前播放時(shí)間,并跳轉(zhuǎn)到當(dāng)前播放時(shí)間進(jìn)行播放。
/** slider拖動(dòng)和點(diǎn)擊事件 */ - (IBAction)touchDownSlider:(UISlider *)sender { // 按下去 移除監(jiān)聽器 [self removeProgressTimer]; [self removeShowTime]; } - (IBAction)valueChangedSlider:(UISlider *)sender { // 計(jì)算slider拖動(dòng)的點(diǎn)對(duì)應(yīng)的播放時(shí)間 NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentItem.duration) * sender.value; self.timeLabel.text = [self timeToStringWithTimeInterval:currentTime]; } - (IBAction)touchUpInside:(UISlider *)sender { [self addProgressTimer]; //計(jì)算當(dāng)前slider拖動(dòng)對(duì)應(yīng)的播放時(shí)間 NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentItem.duration) * sender.value; // seekToTime:播放跳轉(zhuǎn)到當(dāng)前播放時(shí)間 [self.player seekToTime:CMTimeMakeWithSeconds(currentTime, NSEC_PER_SEC) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; [self addShowTime]; }
四. 重播按鈕和全屏播放按鈕的實(shí)現(xiàn)
1、在定時(shí)器每秒調(diào)用的更新Slider的方法中判斷當(dāng)視頻播放完畢之后,顯示遮蓋View,而重播按鈕的實(shí)現(xiàn),其實(shí)就是將Slider的value置為0并重新調(diào)用點(diǎn)擊Slider松開時(shí)的方法,將當(dāng)前播放時(shí)間置為0,重新隱藏遮蓋View,并調(diào)用中間播放按鈕開始播放。
/** 重播按鈕點(diǎn)擊 */ - (IBAction)repeatBtnClick:(UIButton *)sender { self.progressSlider.value = 0; [self touchUpInside:self.progressSlider]; self.coverView.hidden = YES; [self playOrPauseBigBtnClick:self.playOrPauseBigBtn]; }
2、全屏播放的實(shí)現(xiàn)
全屏播放需要控制器Moda出一個(gè)全屏播放的控制器進(jìn)行全屏播放,創(chuàng)建全屏播放控制器CLFullViewController,并使其支持左右方向的旋轉(zhuǎn),Moda出CLFullViewController控制器,并將CLAVPlayerView添加到CLFullViewController的View上并設(shè)置frame即可,當(dāng)退出全屏?xí)r,dismiss掉CLFullViewController然后將CLAVPlayerView的frame設(shè)置為原來的值。
CLFullViewController中設(shè)置可以旋轉(zhuǎn)和旋轉(zhuǎn)方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations { return UIInterfaceOrientationMaskLandscape; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { return YES; }
全屏播放按鈕點(diǎn)擊事件
/** 全屏按鈕點(diǎn)擊事件 */ - (IBAction)fullViewBtnClick:(UIButton *)sender { sender.selected = !sender.selected; [self videoplayViewSwitchOrientation:sender.selected]; } /** 彈出全屏播放器 */ - (void)videoplayViewSwitchOrientation:(BOOL)isFull { if (isFull) { [self.contrainerViewController presentViewController:self.fullVc animated:NO completion:^{ [self.fullVc.view addSubview:self]; self.center = self.fullVc.view.center; [UIView animateWithDuration:0.15 delay:0.0 options:UIViewAnimationOptionLayoutSubviews animations:^{ self.frame = self.fullVc.view.bounds; } completion:nil]; }]; } else { [self.fullVc dismissViewControllerAnimated:NO completion:^{ [self.contrainerViewController.view addSubview:self]; [UIView animateWithDuration:0.15 delay:0.0 options:UIViewAnimationOptionLayoutSubviews animations:^{ self.frame = CGRectMake(0, 200, self.contrainerViewController.view.bounds.size.width, self.contrainerViewController.view.bounds.size.width * 9 / 16); } completion:nil]; }]; } }
注意:這里需要拿到外面控制器來Moda出全屏播放控制器,所以給CLAVPlayerView添加contrainerViewController屬性來拿到控制器。
簡(jiǎn)單封裝
此時(shí)已經(jīng)實(shí)現(xiàn)了播放器基本的功能,接下來考慮如何封裝能使我們使用起來更加方便,其實(shí)我們已經(jīng)將大部分封裝完成,接下來需要做的就是提供簡(jiǎn)單易用的接口,使外部可以輕松調(diào)用實(shí)現(xiàn)播放器。
1、提供類方法快速創(chuàng)建播放器
+ (instancetype)videoPlayView { return [[[NSBundle mainBundle]loadNibNamed:@"CLAVPlayerView" owner:nil options:nil]lastObject]; }
2、播放視頻的資源應(yīng)該由外部決定,因此我們提供urlString屬性用來接收視頻的資源,然后通過重寫其set方法來播放視頻
/** 需要播放的視頻資源set方法 */
-(void)setUrlString:(NSString *)urlString { _urlString = urlString; NSURL *url = [NSURL URLWithString:urlString]; self.playerItem = [AVPlayerItem playerItemWithURL:url]; }
此時(shí)我們?cè)谕獠渴褂貌シ牌骶头浅:?jiǎn)單了,無需考慮內(nèi)部邏輯,只需快速創(chuàng)建CLAVPlayerView,添加到控制器View,設(shè)置其frame,然后指定其播放視頻資源就可以了。
- (void)viewDidLoad { [super viewDidLoad]; [self setUpVideoPlayView]; self.playView.urlString = @"http://120.25.226.186:32812/resources/videos/minion_02.mp4"; } -(void)setUpVideoPlayView { self.playView = [CLAVPlayerView videoPlayView]; self.playView.frame = CGRectMake(0, 200, self.view.frame.size.width, self.view.frame.size.width * 9 / 16); self.playView.contrainerViewController = self; [self.view addSubview:self.playView]; }
最后,視頻播放器大致這個(gè)樣子
總結(jié)
其中還有許多需要完善的地方,一些功能也沒有實(shí)現(xiàn),例如兩個(gè)占位的Button,將來可以用來下載視頻和控制彈幕的開關(guān),播放結(jié)束之后分享按鈕也沒有實(shí)現(xiàn)。以后實(shí)現(xiàn)后給大家繼續(xù)分享,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家能有所幫助,如果有疑問大家可以留言交流。
- iOS開發(fā)之獲取系統(tǒng)相冊(cè)中的圖片與視頻教程(內(nèi)帶url轉(zhuǎn)換)
- IOS實(shí)現(xiàn)視頻動(dòng)畫效果的啟動(dòng)圖
- 淺析iOS中視頻播放的幾種方案
- iOS實(shí)現(xiàn)視頻和圖片的上傳思路
- iOS視頻錄制(或選擇)壓縮及上傳功能(整理)
- iOS仿微信相機(jī)拍照、視頻錄制功能
- 詳解iOS應(yīng)用中播放本地視頻以及選取本地音頻的組件用法
- iOS中讀取照片庫(kù)及保存圖片或視頻到照片庫(kù)的要點(diǎn)解析
- iOS 本地視頻和網(wǎng)絡(luò)視頻流播放實(shí)例代碼
- iOS視頻中斷后臺(tái)音樂播放的處理方法
相關(guān)文章
iOS 隱私權(quán)限和通過openURL實(shí)現(xiàn)跳轉(zhuǎn)實(shí)例
這篇文章主要介紹了iOS 隱私權(quán)限和通過openURL實(shí)現(xiàn)跳轉(zhuǎn)實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06iOS 獲取公歷、農(nóng)歷日期的年月日的實(shí)例代碼
本篇文章主要介紹了iOS 獲取公歷、農(nóng)歷日期的年月日的實(shí)例代碼,主要介紹了三種方法,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-02-02IOS CoreLocation實(shí)現(xiàn)系統(tǒng)自帶定位的方法
本篇文章主要介紹了IOS Core Location實(shí)現(xiàn)系統(tǒng)自帶定位的方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下。2017-02-02IOS開發(fā)代碼分享之獲取啟動(dòng)畫面圖片的string
本文是IOS開發(fā)代碼分享系列的第一篇文章,這里分享下獲取啟動(dòng)畫面圖片的string的代碼,本代碼支持 iPhone 6 以下. 支持 iPhone 及 iPad,非常實(shí)用,希望對(duì)大家有所幫助2014-09-09Apple?Watch?App?Lifecycle應(yīng)用開發(fā)
這篇文章主要為大家介紹了Apple?Watch?App?Lifecycle應(yīng)用開發(fā)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10