iOS Developer程序员ios视频相关

iOS 图解一个功能很全的视频播放器的使用

2018-06-06  本文已影响120人  changsanjiang

大家好, 上一次我分享了一个视频播放器(SJVideoPlayer), 今天我再给大家分享一下它的使用图解, 希望大家喜欢. 对了, 本项目除了全屏手势hook了一下nav的push方法, 其它功能都对原始项目无任何侵害. 您可以放心的使用(全屏手势如果不顺心也可以方便的移除).

上一次分享的文章链接: https://www.jianshu.com/p/4c2a493fb4bf

下面请看这款用心写的播放器吧. 就从最简单的开始吧😝

播放资源的初始化及三板斧

/// 以下为示例:
    _videoPlayer = [SJVideoPlayer player];
    _videoPlayer.view.frame = CGRectMake(0, 20, 375, 375 * 9/16.0); // 可以使用AutoLayout, 这里为了简便设置的Frame.
    [self.view addSubview:_videoPlayer.view];
    // 初始化资源
    _videoPlayer.URLAsset = [[SJVideoPlayerURLAsset alloc] initWithAssetURL:[NSURL URLWithString:@"http://..."]];
    // 当然也可以指定开始时间. 如下, 从第20秒开始播放
    // _videoPlayer.URLAsset = [[SJVideoPlayerURLAsset alloc] initWithAssetURL:[NSURL URLWithString:@"http://..."] beginTime:20.0];

/// 以下为示例:
/// UICollectionView同UITableView初始化一致, 所以此处仅展示UITableView的示例.
- (void)clickedPlayBtnOnTabCell:(SJVideoListTableViewCell *)cell playerSuperview:(UIView *)playerSuperview {
    //  1. 创建一个播放资源
    SJVideoPlayerURLAsset *asset =
    [[SJVideoPlayerURLAsset alloc] initWithAssetURL:[NSURL URLWithString:@"https://..."]
                                          beginTime:20
                                         scrollView:self.tableView
                                          indexPath:[self.tableView indexPathForCell:cell]
                                       superviewTag:playerSuperview.tag]; // 请务必设置tag, 且不能等于0. 由于重用机制, 当视图滚动时, 播放器需要通过此tag寻找其父视图
    // 2. 设置资源标题
    asset.title = @"DIY心情转盘 #手工##手工制作##卖包子喽##1块1个##卖完就撤#";
    // 3. 默认情况下, 小屏时不显示标题, 全屏后才会显示, 这里设置一直显示标题
    asset.alwaysShowTitle = YES;
  
    _videoPlayer = [SJVideoPlayer player];
    [playerSuperview addSubview:_videoPlayer.view];
    [_videoPlayer.view mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.offset(0);
    }];
    // 设置资源
    _videoPlayer.URLAsset = asset;
}

/// 以下为示例:    
    __weak typeof(self) _self = self;
    // table header btn clicked event.
    self.tableHeaderView.clickedPlayBtnExeBlock = ^(TableHeaderView * _Nonnull playerSuperview) {
        __strong typeof(_self) self = _self;
        if ( !self ) return;
        //  1. 创建一个播放资源
        SJVideoPlayerURLAsset *asset =
        [[SJVideoPlayerURLAsset alloc] initWithAssetURL:[NSURL URLWithString:@"https://..."]
                                              beginTime:0
                           playerSuperViewOfTableHeader:playerSuperview
                                              tableView:self.tableView];
        // 2. 设置资源标题
        asset.title = @"DIY心情转盘 #手工##手工制作#";
        // 3. 默认情况下, 小屏时不显示标题, 全屏后才会显示, 这里设置一直显示标题
        asset.alwaysShowTitle = YES;

        self.videoPlayer = [SJVideoPlayer player];
        [playerSuperview addSubview:self.videoPlayer.view];
        [self.videoPlayer.view mas_makeConstraints:^(MASConstraintMaker *make) {
            make.edges.offset(0);
        }];
        // 设置资源
        self.videoPlayer.URLAsset = asset;
    };

/// 以下为示例:
    __weak typeof(self) _self = self;
    _tableHeaderView.clickedPlayBtnExeBlock = ^(TableHeaderCollectionView *view, UICollectionView *collectionView, NSIndexPath *indexPath, UIView *playerSuperview) {
        __strong typeof(_self) self = _self;
        if ( !self ) return;
        //  1. 创建一个播放资源
        SJVideoPlayerURLAsset *asset =
        [[SJVideoPlayerURLAsset alloc] initWithAssetURL:[NSURL URLWithString:@"https://..."]
                                              beginTime:0
                            collectionViewOfTableHeader:collectionView
                                collectionCellIndexPath:indexPath
                                     playerSuperViewTag:playerSuperview.tag
                                          rootTableView:self.tableView];
        
        // 2. 设置资源标题
        asset.title = @"DIY心情转盘 #手工##手工制作#";
        // 3. 默认情况下, 小屏时不显示标题, 全屏后才会显示, 这里设置一直显示标题
        asset.alwaysShowTitle = YES;

        self.videoPlayer = [SJVideoPlayer player];
        [playerSuperview addSubview:self.videoPlayer.view];
        [self.videoPlayer.view mas_makeConstraints:^(MASConstraintMaker *make) {
            make.edges.offset(0);
        }];
        // 设置资源
        self.videoPlayer.URLAsset = asset;
    };

/// 以下为示例:
- (void)clickedPlayWithTableViewCell:(NestedTableViewCell *)tabCell
                     playerSuperview:(UIView *)playerSuperview
         collectionViewCellIndexPath:(NSIndexPath *)collectionViewCellIndexPath
                      collectionView:(UICollectionView *)collectionView {
    //  1. 创建一个播放资源
    NSIndexPath *collectionViewIndexPath = [self.tableView indexPathForCell:tabCell];
    SJVideoPlayerURLAsset *asset =
    [[SJVideoPlayerURLAsset alloc] initWithAssetURL:playURL
                                          beginTime:0
                                          indexPath:collectionViewCellIndexPath
                                       superviewTag:playerSuperview.tag
                                scrollViewIndexPath:collectionViewIndexPath
                                      scrollViewTag:collectionView.tag
                                     rootScrollView:self.tableView];
    // 2. 设置资源标题
    asset.title = @"DIY心情转盘 #手工##手工制作#";
    // 3. 默认情况下, 小屏时不显示标题, 全屏后才会显示, 这里设置一直显示标题
    asset.alwaysShowTitle = YES;

    self.videoPlayer = [SJVideoPlayer player];
    [playerSuperview addSubview:self.videoPlayer.view];
    [self.videoPlayer.view mas_makeConstraints:^(MASConstraintMaker *make) {
            make.edges.offset(0);
    }];
    // 设置资源
    self.videoPlayer.URLAsset = asset;
}

资源三板斧

/// 以下为示例:
    // 对当前资源进行刷新, 尝试重新播放视频
    [_videoPlayer refresh];
/// 以下为示例:
     // 每个资源dealloc时的回调
    _videoPlayer.assetDeallocExeBlock = ^(__kindof SJBaseVideoPlayer * _Nonnull videoPlayer) {
      // .....
    };
+ (instancetype)assetWithOtherAsset:(SJVideoPlayerURLAsset *)asset;
+ (instancetype)assetWithOtherAsset:(SJVideoPlayerURLAsset *)asset
                         scrollView:(__unsafe_unretained UIScrollView * __nullable)tableOrCollectionView
                          indexPath:(NSIndexPath * __nullable)indexPath
                       superviewTag:(NSInteger)superviewTag;
+ (instancetype)assetWithOtherAsset:(SJVideoPlayerURLAsset *)asset
       playerSuperViewOfTableHeader:(__unsafe_unretained UIView *)superView
                          tableView:(__unsafe_unretained UITableView *)tableView;
+ (instancetype)assetWithOtherAsset:(SJVideoPlayerURLAsset *)asset
        collectionViewOfTableHeader:(__unsafe_unretained UICollectionView *)collectionView
            collectionCellIndexPath:(NSIndexPath *)indexPath
                 playerSuperViewTag:(NSInteger)playerSuperViewTag
                      rootTableView:(__unsafe_unretained UITableView *)rootTableView;
+ (instancetype)assetWithOtherAsset:(SJVideoPlayerURLAsset *)asset
                          indexPath:(NSIndexPath *__nullable)indexPath
                       superviewTag:(NSInteger)superviewTag
                scrollViewIndexPath:(NSIndexPath *__nullable)scrollViewIndexPath
                      scrollViewTag:(NSInteger)scrollViewTag
                     rootScrollView:(__unsafe_unretained UIScrollView *__nullable)rootScrollView;

/// 以下为示例:
  // 新界面的播放器, 资源初始化:
    _videoPlayer = [SJVideoPlayer player];
    _videoPlayer.view.frame = CGRectMake(0, 20, 375, 375 * 9/16.0); // 可以使用AutoLayout, 这里为了简便设置的Frame.
    [self.view addSubview:_videoPlayer.view];
    // 初始化资源
    _videoPlayer.URLAsset = [SJVideoPlayerURLAsset assetWithOtherAsset:otherAsset]; 

是的, otherAsset即为上一个页面播放的Asset, 只要用它进行初始化即可实现续播功能. 同时可以发现, 初始化时, 除了需要一个otherAsset, 其他方面同开始的示例一模一样.

请看下图:


image

优雅自如的旋转

对于旋转, 我们开发者肯定需要绝对的控制, 例如: 设置自动旋转所支持方向. 能够主动+自动旋转, 而且还需要能在适当的时候禁止自动旋转. 旋转前后的回调等等... 放心这些功能都有, 我挨个给大家介绍一下:

先说说何为自动旋转. 其实就是播放器根据当前设备的方向, 进行自动旋转.

/// 自动旋转所支持的方向
typedef NS_ENUM(NSUInteger, SJAutoRotateSupportedOrientation) {
    SJAutoRotateSupportedOrientation_All,
    SJAutoRotateSupportedOrientation_Portrait = 1 << 0,
    SJAutoRotateSupportedOrientation_LandscapeLeft = 1 << 1,  // UIDeviceOrientationLandscapeLeft
    SJAutoRotateSupportedOrientation_LandscapeRight = 1 << 2, // UIDeviceOrientationLandscapeRight
};

以上为自动旋转时, 所支持的方向, 播放器默认为SJAutoRotateSupportedOrientation_All. 当我们不想让播放器旋转到某个方向时, 可以如下设置:

/// 以下为示例:
    // 例如设置播放器只能在全屏方向上旋转
    _videoPlayer.supportedOrientation = SJAutoRotateSupportedOrientation_LandscapeLeft | SJAutoRotateSupportedOrientation_LandscapeRight;

请看以下方法, 分别对应以上三点:


- (void)rotate;
- (void)rotate:(SJOrientation)orientation animated:(BOOL)animated;
- (void)rotate:(SJOrientation)orientation animated:(BOOL)animated completion:(void (^ _Nullable)(__kindof SJBaseVideoPlayer *player))block;

// 调用示例:
[_videoPlayer rotate]; // 主动旋转, 让播放器旋转到用户当前的设备方向或小屏.

/// 旋转前的回调
@property (nonatomic, copy, nullable) void(^willRotateScreen)(__kindof SJBaseVideoPlayer *player, BOOL isFullScreen);
/// 旋转后的回调
@property (nonatomic, copy, nullable) void(^rotatedScreen)(__kindof SJBaseVideoPlayer *player, BOOL isFullScreen);

/// 以下为示例:

// 旋转前的示例(我常用旋转前的block, 旋转后的block基本没用过😝):
// 1. 设置播放器旋转前的回调. 
    _videoPlayer.willRotateScreen = ^(SJVideoPlayer * _Nonnull player, BOOL isFullScreen) {
        __strong typeof(_self) self = _self;
        if ( !self ) return ;
        [UIView animateWithDuration:0.25 animations:^{
            [self setNeedsStatusBarAppearanceUpdate];
        }];
    };
// 2. 根据控制层的显示状态 去控制状态栏的显示和隐藏
- (BOOL)prefersStatusBarHidden {
  // 全屏播放时, 使状态栏根据控制层显示或隐藏
  if ( self.videoPlayer.isFullScreen ) return !self.videoPlayer.controlLayerAppeared;
  return NO;
}
// 3. 如果播放器为全屏显示时, 返回状态栏的style为UIStatusBarStyleLightContent, 小屏返回 UIStatusBarStyleDefault
- (UIStatusBarStyle)preferredStatusBarStyle {
  // 全屏播放时, 使状态栏变成白色
  if ( self.videoPlayer.isFullScreen ) return UIStatusBarStyleLightContent;
  return UIStatusBarStyleDefault;
}

// 禁止自动旋转. 
_videoPlayer.disableAutoRotation = YES;

这里有两点需要注意: 1. 返回时要记得恢复自动旋转. 2. 禁止自动旋转后, 手动点击全屏按钮, 还是可以旋转的.


请注意: 在锁屏状态下, 此时不管是主动旋转, 还是自动旋转, 都将不触发. 代码如下:

/// 锁屏
_videoPlayer.lockedScreen = YES;

/// 是否是全屏
@property (nonatomic, readonly) BOOL isFullScreen;
/// 当前播放器的方向
@property (nonatomic) SJOrientation orientation;
/// 当前播放器旋转到的设备方向
@property (nonatomic, readonly) UIInterfaceOrientation currentOrientation;

播放的控制

SJVideoPlayer的常规播放控制大概有: 静音, 自动播放, 使播放, 使暂停, 使停止, 使重播.
哦, 对了还有亮度, 声音, 速率(rate)这些的设置. 并且都有相应的回调. 代码我就不贴了, 一看就明白了.

我再介绍一下其他的控制功能:

/**
 关于后台播放视频, 引用自: https://juejin.im/post/5a38e1a0f265da4327185a26
 
 当您想在后台播放视频时:
 1. 需要设置 videoPlayer.pauseWhenAppDidEnterBackground = NO; (该值默认为YES, 即App进入后台默认暂停).
 2. 前往 `TARGETS` -> `Capability` -> enable `Background Modes` -> select this mode `Audio, AirPlay, and Picture in Picture`
 */
@property (nonatomic) BOOL pauseWhenAppDidEnterBackground;

// 示例:
_videoPlayer.pauseWhenAppDidEnterBackground = YES; // 请记得按上述注释的步骤配置

    __weak typeof(self) _self = self;
    _videoPlayer.playDidToEnd = ^(__kindof SJBaseVideoPlayer * _Nonnull player) {
        __strong typeof(_self) self = _self;
        if ( !self ) return ;
        [player replay];
    }

如上, 当播放完毕时, 播放器调用 replay 方法, 让其从头重新开始播放.


网络状态变更时的提示

有时候我们需要能够友好的告诉客户当前的网络状态发生了改变, 毕竟流量是要钱的. 我们继续看图:


网络状态变更提示.png

这些提示, 我都做了本地化处理, 支持的语言有: 中文/繁体/英文. 开发者也可以自己定义想要的提示. 后面我会介绍SJVideoPlayer全局的配置类, 它可以配置各个控件的图片, slider, 本地化的一些提示等等.


夜已深, 请看下回

由于内容比较多, 就介绍到这里吧(熬夜到1点了... 明天还要上班😔), 下一篇我会介绍全局的配置类, 还有如何无缝的替换掉原始控制层, 同时保留自己想要的控制层(切换器).

对了最后唠叨一下, 恳请尊重开源作者的劳动成果, 引用或参照还请注明来源. 谢谢.

另外这是我的联系方式, 希望与您交流:

项目地址: https://github.com/changsanjiang/SJVideoPlayer

我的邮箱: changsanjiang@gmail.com

如果您有什么建议, 望请联系我!

上一篇下一篇

猜你喜欢

热点阅读