自定义AVPlayer视频播放器

这次自定义一个视频播放器
先看效果:

段宜恩.gif

功能如下:

1播放视频 (感觉是废话)
2播放暂停
3放大缩小
4工具条自动隐藏,点击显示
5进度条随时间移动
6点击,拖拽移动条显示不同数据

前四个比较简单放到一起讲,顺带把滑动条也加上
先引用

import

需要注册的属性

//   视屏播放器背景图
@property (strong, nonatomic) UIView *backView;
//   播放视频控件
@property (nonatomic,strong) AVPlayer *player;
//   提供视频信息   创建AVPlayer使用的
@property (nonatomic,strong) AVPlayerItem *playerItem;
//   给AVPlayer一个播放的layer层
@property (nonatomic,strong) AVPlayerLayer *playerLayer;
//   工具栏 view  (放播放按钮等控件的view)
@property (nonatomic,strong) UIView *bottomView;
//   播放
@property (nonatomic,strong) UIButton *playButton;
//   全屏按钮
@property (nonatomic,strong) UIButton *fullScreenButton;
//   滑动条
@property (nonatomic,strong) UISlider *slider;

上代码

   // 初始化背景
    self.backView = [[UIView alloc] initWithFrame:CGRectMake(0, 100, kScreenWidth, kScreenHeight / 2.5)];
    self.backView.backgroundColor = [UIColor clearColor];
    [self.view addSubview:self.backView];

    // 提供视频信息   创建AVPlayer使用的
    self.playerItem = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:@"http://static.tripbe.com/videofiles/20121214/9533522808.f4v.mp4"]];
    //  通过AVPlayerItem创建AVPlayer
    self.player = [[AVPlayer alloc] initWithPlayerItem:self.playerItem];
    // 单纯使用AVPlayer类是无法显示视频的,要将视频层添加至AVPlayerLayer中,这样才能将视频显示出来  所以要给AVPlayer一个播放的layer层
    _playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
    //  位置
    _playerLayer.frame = self.backView.bounds;
    //    插入指定位置             第二个参数,可以将一个图层插入到指定的下标位置
    [self.backView.layer insertSublayer:self.playerLayer atIndex:0];

    //添加手势动作,隐藏下面的进度条
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)];
    [self.backView addGestureRecognizer:tap];
    
    // 布局底部功能栏
    self.bottomView = [[UIView alloc] init];
    //  透明度
    self.bottomView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.6];
    [self.backView addSubview:self.bottomView];
    [self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.backView).with.offset(0);
        make.right.equalTo(self.backView).with.offset(0);
        make.bottom.equalTo(self.backView).with.offset(0);
        make.height.mas_equalTo(30);
    }];
    
    // 播放或暂停按钮
    self.playButton = [UIButton buttonWithType:UIButtonTypeCustom];
    //  选中状态
    self.playButton.selected = YES;
    [self.playButton setImage:[UIImage imageNamed:@"pause"] forState:UIControlStateNormal];
    [self.bottomView addSubview:self.playButton];
    [self.playButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.bottomView).with.offset(5);
        make.centerY.equalTo(self.bottomView);
        make.size.mas_equalTo(CGSizeMake(30, 30));
    }];
    
    //  添加点击播放事件
    [self.playButton addTarget:self action:@selector(pauseOrPlay:) forControlEvents:UIControlEventTouchUpInside];
   
    
    //  底部全屏按钮
    self.fullScreenButton = [UIButton buttonWithType:UIButtonTypeCustom];
    [self.fullScreenButton setImage:[UIImage imageNamed:@"fullscreen"] forState:UIControlStateNormal];
    self.fullScreenButton.selected = YES;
    [self.bottomView addSubview:self.fullScreenButton];
    [self.fullScreenButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.right.equalTo(self.bottomView).with.offset(-5);
        make.centerY.equalTo(self.bottomView);
        make.size.mas_equalTo(CGSizeMake(30, 30));
    }];
    //   添加点击全屏事件
    [self.fullScreenButton addTarget:self action:@selector(clickFullScreen:) forControlEvents:UIControlEventTouchUpInside];
    
    // 底部进度条
    self.slider = [[UISlider alloc] init];
    //   已播放的部分颜色
    self.slider.minimumTrackTintColor = [UIColor whiteColor];
    //  未播放部分的进度条颜色
    self.slider.maximumTrackTintColor = [UIColor grayColor];
    // 初始位置  默认是0
    self.slider.value = 0.0;
    [self.slider setThumbImage:[UIImage imageNamed:@"dot"] forState:UIControlStateNormal];
    [self.bottomView addSubview:self.slider];
    [self.slider mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.bottomView).with.offset(45);
        make.right.equalTo(self.bottomView).with.offset(-45);
        make.centerY.equalTo(self.bottomView);
    }];
 
    //  播放视频
    [self.player play];

布局基本就全了,在添加上对应的点击事件

#pragma mark ==== 暂停或者播放
- (void)pauseOrPlay:(UIButton *)sender{
    
    if (sender.selected == YES)
    {
        [sender setImage:[UIImage imageNamed:@"play"] forState:UIControlStateNormal];
        //  暂停
        [self.player pause];
    }
    else
    {
        [sender setImage:[UIImage imageNamed:@"pause"] forState:UIControlStateNormal];
        [self.player play];
    }
    // 别忘记切换 状态
    sender.selected = !sender.selected;
}


#pragma mark ==== 点击全屏按钮
- (void)clickFullScreen:(UIButton *)button{
    
    if (button.selected == YES)
    {   // 全屏
        [self toFullScreenWithInterfaceOrientation:UIInterfaceOrientationLandscapeLeft];
        [self.fullScreenButton setImage:[UIImage imageNamed:@"nonfullscreen@3x"] forState:UIControlStateNormal];
    }
    else
    {
        //  缩小
        [self toSmallScreen];
        [self.fullScreenButton setImage:[UIImage imageNamed:@"fullscreen@3x"] forState:UIControlStateNormal];
    }
    button.selected = !button.selected;
}


#pragma mark ====   全屏显示
- (void)toFullScreenWithInterfaceOrientation:(UIInterfaceOrientation )interfaceOrientation{

    if (interfaceOrientation==UIInterfaceOrientationLandscapeLeft) {
        // 选择屏幕
        self.backView.transform = CGAffineTransformMakeRotation(-M_PI_2);
    }
    // BackView 全屏
    self.backView.frame = CGRectMake(0, 0, kScreenWidth, kScreenHeight);
    // layer的方向宽和高对调
    self.playerLayer.frame = CGRectMake(0, 0, kScreenHeight, kScreenWidth);
    
    [self.bottomView mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.height.mas_equalTo(50);
        make.top.mas_equalTo(kScreenWidth-50);
        make.left.equalTo(self.backView).with.offset(0);
        make.width.mas_equalTo(kScreenHeight);
    }];
    // 加到window上面
    [[UIApplication sharedApplication].keyWindow addSubview:self.backView];
    
}


#pragma mark ==== 缩小
- (void)toSmallScreen{

        self.backView.transform = CGAffineTransformIdentity;
        self.backView.frame = CGRectMake(0, 100, kScreenWidth, kScreenHeight / 2.5);
        self.playerLayer.frame =  self.backView.bounds;
        [self.view addSubview:self.backView];
        
        [self.bottomView mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self.backView).with.offset(0);
            make.right.equalTo(self.backView).with.offset(0);
            make.height.mas_equalTo(50);
            make.bottom.equalTo(self.backView).with.offset(0);
        }];
}

#pragma mark ====  隐藏 显示  底部 工具栏
- (void)singleTap:(UITapGestureRecognizer *)tap{
    
    [UIView animateWithDuration:1.0 animations:^{
        if (self.bottomView.alpha == 1)
        {
            self.bottomView.alpha = 0;
        }
        else if (self.bottomView.alpha == 0)
        {
            self.bottomView.alpha = 1;
        }
    }];
}

到此前四项只有4的自动隐藏还没有完成

把功能拆开想:他不是立刻隐藏,而是停顿了一会再隐藏的,这需要计时器来设置时间,看视频播放软件暂停的时候是不隐藏的,那就是需要播放了一定时间再隐藏,这就需要监听播放器

先写个时间属性,基于安全考虑设为属性好释放

//   自动消失定时器
@property (nonatomic,strong) NSTimer *autoDismissTimer;

监听视频播放器,用KVO实现

  // 监听播放器状态变化   利用KVO 监听  只要属性一改变就会调用监听方法  暂停时不会更新,所以暂停不会继续调用通知
    [self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];

对应的监听方法

#pragma mark ==== 监听播放器的变化属性
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    //  播放时
    if ([keyPath isEqualToString:@"status"])
    {
        //  播放器状态
        AVPlayerItemStatus statues = [change[NSKeyValueChangeNewKey] integerValue];
        switch (statues) {
            case AVPlayerItemStatusReadyToPlay:
                //  滑动条最大值  =   持续时间(播放总时间)
                self.slider.maximumValue = CMTimeGetSeconds(self.playerItem.duration);
                // 随时间移动
                [self initTimer];
               
                //  计时器为空时创建计时器
                if (!self.autoDismissTimer)
                {
                     // 8秒执行一次  自动隐藏底栏
                    self.autoDismissTimer = [NSTimer timerWithTimeInterval:8.0 target:self selector:@selector(autoDismissView:) userInfo:nil repeats:YES];
                    // 主线程 不需要 return
                    [[NSRunLoop currentRunLoop] addTimer:self.autoDismissTimer forMode:NSDefaultRunLoopMode];
                }
                break;
            case AVPlayerItemStatusUnknown:
                //   未知状态
                break;
            case AVPlayerItemStatusFailed:
                //    播放失败
                break;
                
            default:
                break;
        }
    }
}


//调用plaer进行页面更新
- (void)initTimer
{
    //player的定时器
    __weak typeof(self)weakSelf = self;
    // 每秒更新一次UI Slider            CMTimeMake(表示 当前视频播放到的第几桢数,每秒的帧数)
    [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
            weakSelf.slider.value = CMTimeGetSeconds(weakSelf.playerItem.currentTime);
    }];
}


//自动隐藏底部功能栏
- (void)autoDismissView:(NSTimer *)timer{
    // 0  暂停   1时正在播放视频
    if (self.player.rate == 1)
    {
        [UIView animateWithDuration:2.0 animations:^{
            self.bottomView.alpha = 0;
        }];
    }
}

现在只剩下6了
拆开分析,点击播放应当是需要点击手势的,拖动还是用监听比较好

点击手势

    //   点击
    UITapGestureRecognizer *tapSlider = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(touchSlider:)];
    [self.slider addGestureRecognizer:tapSlider];
    
    [self.bottomView addSubview:self.slider];

拖动监听

    [self.slider addTarget:self action:@selector(sliderTapValueChange:) forControlEvents:UIControlEventTouchUpInside];

对应的方法

#pragma mark ==== 点击调用  或者 拖拽完毕的时候调用
- (void)sliderTapValueChange:(UISlider *)slider
{
    //  seektotime  获取精准定位
    // 直接用秒来获取CMTime
    // 当前视频播放到的帧数的具体时间,每秒的帧数
    [self.player seekToTime:CMTimeMakeWithSeconds(slider.value, self.playerItem.currentTime.timescale)];
}


#pragma mark    ====  点击Slider
- (void)touchSlider:(UITapGestureRecognizer *)tap
{
    // 根据点击的坐标计算对应的比例
    CGPoint touch = [tap locationInView:self.slider];
    //  占总厂的比例
    CGFloat scale = touch.x / self.slider.bounds.size.width;
    //CMTimeGetSeconds  获取时间的秒数
    self.slider.value = CMTimeGetSeconds(self.playerItem.duration) * scale;
    //  精准  具体时间,每秒的帧数
    [self.player seekToTime:CMTimeMakeWithSeconds(self.slider.value, self.playerItem.currentTime.timescale)];

}

再考虑一种情况暂停时拖拽和点击时

- (void)touchSlider:(UITapGestureRecognizer *)tap
{
    // 根据点击的坐标计算对应的比例
    CGPoint touch = [tap locationInView:self.slider];
    //  占总厂的比例
    CGFloat scale = touch.x / self.slider.bounds.size.width;
    //CMTimeGetSeconds  获取时间的秒数
    self.slider.value = CMTimeGetSeconds(self.playerItem.duration) * scale;
    //  精准  具体时间,每秒的帧数
    [self.player seekToTime:CMTimeMakeWithSeconds(self.slider.value, self.playerItem.currentTime.timescale)];
    if (self.player.rate != 1)
    {
        // 1的时候播放   0暂停
        [self.playButton setImage:[UIImage imageNamed:@"pause"] forState:UIControlStateNormal];
        [self.player play];
    }
}

这样就可以了

到此自定义AVPlayer视频播放器功能已全部实现

附上下载地址
https://gitee.com/wwwzz/ZiDingYiAVPlayerShiPinBoFangQi.git

你可能感兴趣的:(自定义AVPlayer视频播放器)