一.創(chuàng)建一個(gè)類用來顯示進(jìn)度信息
1.創(chuàng)建一個(gè)繼承于UIView的類SliderView
2.定義我們需要?jiǎng)?chuàng)建的四個(gè)視圖變量
/**容器視圖*/
@property (nonatomic,strong) UIView *containerView;
/**未播放進(jìn)度視圖*/
@property (nonatomic,strong) UIImageView *bgProgressView;
/**已播放進(jìn)度視圖*/
@property (nonatomic,strong) UIImageView *tintProgressView;
/**進(jìn)度點(diǎn)視圖*/
@property (nonatomic,strong) UIImageView *dotProgressView;
3.因?yàn)橛卸鄠€(gè)視圖要?jiǎng)?chuàng)建司倚,所以我們抽出來形成一個(gè)方法
#pragma mark -------返回一個(gè)圖片視圖 ---------
-(UIImageView *)viewWithFrame:(CGRect)frame color:(UIColor *)color{
//創(chuàng)建視圖
UIImageView *imgView = [[UIImageView alloc] initWithFrame:frame];
//設(shè)置背景顏色
imgView.backgroundColor = color;
//顯示
[self.containerView addSubview:imgView];
return imgView;
}
4.重寫initWithFrame方法,創(chuàng)建四個(gè)視圖
#pragma mark -------重寫initWithFrame方法 布局 ---------
-(instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
//創(chuàng)建容器視圖
self.containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame .size.height)];
//背景顏色
_containerView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.2];
//顯示
[self addSubview:_containerView];
//未播放進(jìn)度視圖
self.bgProgressView = [self viewWithFrame:CGRectMake(kSize, (self.frame.size.height-kProgressHeight)/2.0, frame.size.width-2*kSize, kProgressHeight) color:[UIColor lightGrayColor]];
//圓角
_bgProgressView.layer.cornerRadius = 2.5;
//已播放進(jìn)度視圖
self.tintProgressView = [self viewWithFrame:CGRectMake(kSize, (self.frame.size.height-kProgressHeight)/2.0, 0, kProgressHeight) color:[UIColor orangeColor]];
//圓角
_tintProgressView.layer.cornerRadius = 2.5;
//進(jìn)度點(diǎn)視圖
self.dotProgressView = [self viewWithFrame:CGRectMake(0, 0, 16, 16) color:[UIColor orangeColor]];
//進(jìn)度點(diǎn)視圖中心點(diǎn)移動(dòng)到最左邊
_dotProgressView.center = CGPointMake(kSize, self.frame.size.height/2.0);
//設(shè)置圓角
_dotProgressView.layer.cornerRadius = 8;
}
return self;
}
5.在initWithFrame方法中添加滑動(dòng)手勢(shì)
//添加滑動(dòng)手勢(shì)
UIPanGestureRecognizer *panGes = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
[self addGestureRecognizer:panGes];
//設(shè)置進(jìn)度條的初始狀態(tài)是正常
self.status = kProgressStatusNormal;
二.創(chuàng)建一個(gè)視圖用來顯示播放器
1.創(chuàng)建一個(gè)繼承于UIView的類PlayerView
2.用一個(gè)類方法來創(chuàng)建該類榛搔,并定義一個(gè)變量來保存?zhèn)鬟f過來的視頻的urlString
/**保存?zhèn)鬟f過來的URL字符串*/
@property (nonatomic,strong) NSString *urlString;
//創(chuàng)建播放器視圖
+(PlayerView *)playerViewFrame:(CGRect)frame url:(NSString *)urlString{
//創(chuàng)建
PlayerView *playerView = [[PlayerView alloc] initWithFrame:frame];
//設(shè)置背景顏色
playerView.backgroundColor = [UIColor grayColor];
//保存url
playerView.urlString = urlString;
return playerView;
}
3.重寫initWithFrame方法棚贾,創(chuàng)建進(jìn)度條視圖,控制播放的按鈕,顯示的文本
//重寫initWithFrame方法 布局
-(instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
//創(chuàng)建進(jìn)度條
self.slider = [[SliderView alloc] initWithFrame:CGRectMake(0, self.frame.size.height-kSliderHeight, self.frame.size.width, kSliderHeight)];
[self addSubview:_slider];
//控制播放的按鈕
self.controlBtn = [UIButton buttonWithType:UIButtonTypeCustom];
_controlBtn.frame = CGRectMake(0, 0, 22, 22);
_controlBtn.center = CGPointMake(kSize/2.0, _slider.frame.size.height/2.0);
[_controlBtn setBackgroundImage:[UIImage imageNamed:@"play"] forState:UIControlStateNormal];
[_controlBtn addTarget:self action:@selector(changeStatus:) forControlEvents:UIControlEventTouchUpInside];
[_slider addSubview:_controlBtn];
//顯示時(shí)間的文本
self.timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.slider.frame.size.width-kSize, 0, kSize, self.slider.frame.size.height)];
_timeLabel.text = @"00:00";
_timeLabel.textColor = [UIColor whiteColor];
_timeLabel.font = [UIFont fontWithName:@"Helvetica" size:16];
_timeLabel.textAlignment = NSTextAlignmentCenter;
[_slider addSubview:_timeLabel];
}
return self;
}
//改變播放狀態(tài)
-(void)changeStatus:(UIButton *)sender{
if (_player.rate == 0) {
//播放
[sender setBackgroundImage:[UIImage imageNamed:@"pause"] forState:UIControlStateNormal];
[self play];
}else{
//暫停
[sender setBackgroundImage:[UIImage imageNamed:@"play"] forState:UIControlStateNormal];
[self pause];
}
}
//播放
-(void)play{
[_player play];
}
//暫停
-(void)pause{
[_player pause];
}
AVPlayer
有一個(gè)屬性rate
表示視頻播放的速度祈纯,所以當(dāng)rate為0時(shí),可以判斷視頻不在播放
4.當(dāng)urlString一有了數(shù)據(jù)叼耙,就可以用來播放了
//重寫urlString的set方法
-(void)setUrlString:(NSString *)urlString{
_urlString = urlString;
//創(chuàng)建播放器
self.player = [AVPlayer playerWithURL:[NSURL URLWithString:urlString]];
//創(chuàng)建顯示圖層
AVPlayerLayer *layer = [AVPlayerLayer playerLayerWithPlayer:_player];
layer.frame = self.bounds;
[self.layer insertSublayer:layer atIndex:0];
//防止循環(huán)引用
__block typeof(self) weakSelf = self;
//監(jiān)聽播放進(jìn)度改變的消息
[_player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(1, NSEC_PER_SEC) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
//獲取當(dāng)前播放的比例
CGFloat progress = CMTimeGetSeconds(_player.currentTime)/CMTimeGetSeconds(_player.currentItem.duration);
//改變滑動(dòng)視圖
weakSelf.slider.progress = progress;
//改變時(shí)間
int playTime = CMTimeGetSeconds(_player.currentItem.duration)*progress;
weakSelf.timeLabel.text = [weakSelf timeStringWithSecond:playTime];
}];
}
AVPlayer 給我們直接提供了觀察播放進(jìn)度的方法-添加周期時(shí)間觀察者,簡(jiǎn)而言之就是腕窥,每隔一段時(shí)間后執(zhí)行 block
- (id)addPeriodicTimeObserverForInterval:(CMTime)interval
queue:(nullable dispatch_queue_t)queue
usingBlock:(void (^)(CMTime time))block;
使用這個(gè)方法還需要了解專門用于標(biāo)識(shí)電影時(shí)間的結(jié)構(gòu)體CMTime
typedef struct{
CMTimeValue value; // 幀數(shù)
CMTimeScale timescale; // 幀率(影片每秒有幾幀)
CMTimeFlags flags;
CMTimeEpoch epoch;
} CMTime;
AVPlayerItem 的 duration 屬性就是一個(gè) CMTime 類型的數(shù)據(jù)。 如果我們想要獲取影片的總秒數(shù)那么就可以用 duration.value / duration.timeScale 計(jì)算出來筛婉,也可以使用 CMTimeGetSeconds 函數(shù)
double seconds = CMTimeGetSeconds(item.duration);
// 相當(dāng)于 duration.value / duration.timeScale
如果一個(gè)影片為60frame(幀)每秒簇爆, 當(dāng)前想要跳轉(zhuǎn)到 120幀的位置,也就是兩秒的位置爽撒,那么就可以創(chuàng)建一個(gè) CMTime 類型數(shù)據(jù)
CMTime,通常用如下兩個(gè)函數(shù)來創(chuàng)建
CMTimeMake(int64_t value, int32_t scale)
CMTime time1 = CMTimeMake(120, 60);
CMTimeMakeWithSeconds(Flout64 seconds, int32_t scale)
CMTime time2 = CMTimeWithSeconds(120, 60);
CMTimeMakeWithSeconds 和CMTimeMake 區(qū)別在于入蛆,第一個(gè)函數(shù)的第一個(gè)參數(shù)可以是float,其他一樣
5.SliderView接受PlayerView傳來的比例硕勿,更改已播放視圖的寬度以及點(diǎn)視圖的位置
/**接受視頻播放的比例*/
@property (nonatomic,assign) CGFloat progress;
#pragma mark -------重寫progress的set方法哨毁,隨著視頻的播放更改進(jìn)度 ---------
-(void)setProgress:(CGFloat)progress{
_progress = progress;
//更改進(jìn)度
[self seekToPoint:progress*self.bgProgressView.frame.size.width];
}
#pragma mark -------進(jìn)度點(diǎn)移動(dòng)以及拖拽狀態(tài)下視頻內(nèi)容隨之改變 ---------
-(void)seekToPoint:(CGFloat)current{
//在合理的范圍之內(nèi)
if (current >= 0 && current <= self.bgProgressView.frame.size.width) {
//更改已播放進(jìn)度視圖的寬度
_tintProgressView.frame = CGRectMake(kSize, (self.frame.size.height-kProgressHeight)/2.0, current, kProgressHeight);
//更改進(jìn)度點(diǎn)的位置
_dotProgressView.center = CGPointMake(kSize+current, self.frame.size.height/2.0);
}
}
6.當(dāng)我們手動(dòng)拖拽進(jìn)度條的時(shí)候需要回調(diào)給播放器,拖拽的進(jìn)度首尼。并且拖拽的時(shí)候挑庶,不能播放,所以我們需要定義一個(gè)枚舉判斷當(dāng)前進(jìn)度條的狀態(tài)软能,并回調(diào)給播放器
相關(guān)信息的定義
//判斷當(dāng)前進(jìn)度條的狀態(tài)
typedef NS_ENUM(NSInteger,kProgressStatus) {
kProgressStatusDrag, //拖拽
kProgressStatusNormal //正常
};
//定義一個(gè)block迎捺,用于返回拖拽的比例從而設(shè)置視頻的跳轉(zhuǎn)
typedef void(^SliderBlock)(CGFloat progress);
//定義一個(gè)block,用于返回進(jìn)度條是否被拖拽查排,進(jìn)度條被拖拽的時(shí)候是不播放的
typedef void(^ProgressStausBlock)(kProgressStatus status);
/**定義一個(gè)回調(diào)拖拽比例的block類型的變量*/
@property (nonatomic,copy) SliderBlock sliderBlock;
/**定義一個(gè)回調(diào)進(jìn)度條狀態(tài)的block類型的變量*/
@property (nonatomic,copy) ProgressStausBlock statusBlock;
首先在initWithFrame里面設(shè)置當(dāng)前進(jìn)度條的狀態(tài)
//設(shè)置進(jìn)度條的初始狀態(tài)是正常
self.status = kProgressStatusNormal;
定義拖動(dòng)手勢(shì)的方法
#pragma mark -------滑動(dòng)手勢(shì) 拖動(dòng)快進(jìn)快退 ---------
-(void)pan:(UIPanGestureRecognizer *)panGesture{
//獲得觸摸點(diǎn)
CGPoint location = [panGesture locationInView:self.bgProgressView];
if (panGesture.state == UIGestureRecognizerStateBegan) {
//開始滑動(dòng)
//進(jìn)入拖拽狀態(tài) 暫停播放
self.status = kProgressStatusDrag;
if (self.statusBlock) {
self.statusBlock(kProgressStatusDrag);
}
}else if(panGesture.state == UIGestureRecognizerStateChanged){
//滑動(dòng)過程中
[self seekToPoint:location.x];
}else if(panGesture.state == UIGestureRecognizerStateEnded){
//滑動(dòng)結(jié)束
//進(jìn)入正常狀態(tài) 開始播放
self.status = kProgressStatusNormal;
if (self.statusBlock) {
self.statusBlock(kProgressStatusNormal);
}
}
}
完善seekToPoint方法
#pragma mark -------進(jìn)度點(diǎn)移動(dòng)以及拖拽狀態(tài)下視頻內(nèi)容隨之改變 ---------
-(void)seekToPoint:(CGFloat)current{
//在合理的范圍之內(nèi)
if (current >= 0 && current <= self.bgProgressView.frame.size.width) {
//更改已播放進(jìn)度視圖的寬度
_tintProgressView.frame = CGRectMake(kSize, (self.frame.size.height-kProgressHeight)/2.0, current, kProgressHeight);
//更改進(jìn)度點(diǎn)的位置
_dotProgressView.center = CGPointMake(kSize+current, self.frame.size.height/2.0);
//拖拽情況下才需要跳轉(zhuǎn)視頻
if (self.status == kProgressStatusDrag) {
if (self.sliderBlock) {
self.sliderBlock(current/self.bgProgressView.frame.size.width);
}
}
}
}
7.播放器接受相關(guān)信息的回調(diào)并作出相關(guān)的反應(yīng)
//重寫initWithFrame方法 布局
-(instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
//創(chuàng)建進(jìn)度條
self.slider = [[SliderView alloc] initWithFrame:CGRectMake(0, self.frame.size.height-kSliderHeight, self.frame.size.width, kSliderHeight)];
[self addSubview:_slider];
//防止循環(huán)引用
__block typeof(self) weakSelf = self;
//進(jìn)度條的狀態(tài)
[_slider setStatusBlock:^(kProgressStatus status) {
if (status == kProgressStatusNormal) {
//正常播放
[weakSelf play];
//調(diào)整按鈕圖片
[weakSelf.controlBtn setBackgroundImage:[UIImage imageNamed:@"pause"] forState:UIControlStateNormal];
}else{
//暫停播放
[weakSelf pause];
//調(diào)整按鈕圖片
[weakSelf.controlBtn setBackgroundImage:[UIImage imageNamed:@"play"] forState:UIControlStateNormal];
}
}];
//進(jìn)度條拖拽的比例
[_slider setSliderBlock:^(CGFloat progress) {
//獲得視頻應(yīng)該顯示那一片段的時(shí)間點(diǎn)
int time = CMTimeGetSeconds(self.player.currentItem.duration)*progress;
//跳轉(zhuǎn)到相應(yīng)的片段
[weakSelf.player seekToTime:CMTimeMake(time*NSEC_PER_SEC, NSEC_PER_SEC) completionHandler:^(BOOL finished) {
}];
}];
}
return self;
}
8.通過點(diǎn)擊進(jìn)度視圖凳枝,達(dá)到快進(jìn)快退的效果
#pragma mark -------觸摸事件 點(diǎn)擊快進(jìn)快退---------
//觸摸開始
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//獲得觸摸點(diǎn)
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self.bgProgressView];
//暫停播放
self.status = kProgressStatusDrag;
if (self.statusBlock) {
self.statusBlock(kProgressStatusDrag);
}
//跳轉(zhuǎn)
[self seekToPoint:location.x];
}
//觸摸結(jié)束
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//開始播放
self.status = kProgressStatusNormal;
if (self.statusBlock) {
self.statusBlock(kProgressStatusNormal);
}
}
9.加載播放器視圖
- (void)viewDidLoad {
[super viewDidLoad];
//創(chuàng)建
self.playerView = [PlayerView playerViewFrame:CGRectMake(0, (self.view.frame.size.height-Height)/2.0, self.view.frame.size.width, Height) url:@"http://127.0.0.1/upLoad/video/abc.mov"];
//顯示
[self.view addSubview:_playerView];
}
三.運(yùn)行結(jié)果
demo鏈接
https://pan.baidu.com/s/1kITSz83zkCTQg86ZPOJfJA 密碼:3kn8
參考文章
http://www.reibang.com/p/6cb137340732 -基本使用