iOS 图解一个功能很全的视频播放器的使用
大家好, 上一次我分享了一个视频播放器(SJVideoPlayer), 今天我再给大家分享一下它的使用图解, 希望大家喜欢. 对了, 本项目除了全屏手势hook了一下nav的push方法, 其它功能都对原始项目无任何侵害. 您可以放心的使用(全屏手势如果不顺心也可以方便的移除).
上一次分享的文章链接: https://www.jianshu.com/p/4c2a493fb4bf
下面请看这款用心写的播放器吧. 就从最简单的开始吧😝
播放资源的初始化及三板斧
-
播放器在普通视图上播放:
在UIView上播放.png
/// 以下为示例:
_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];
-
播放器在UITableViewCell或UICollectionViewCell中播放:
播放器在UITableViewCell或UICollectionViewCell中播放.png
/// 以下为示例:
/// 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;
}
-
播放器在tableHeaderView上播放:
播放器在tableHeaderView上播放.png
/// 以下为示例:
__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;
};
-
播放器在UICollectionViewCell中播放, 同时UICollectionView在tableHeaderView中:
播放器在UICollectionViewCell中播放, 同时UICollectionView在tableHeaderView中.png
/// 以下为示例:
__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;
};
-
播放器在UICollectionCell中播放, 同时UICollectionView在UITableViewCell中:
播放器在UICollectionCell中播放, 同时UICollectionView在UITableViewCell中.png
/// 以下为示例:
- (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;
}
资源三板斧
- 资源刷新. 在播放一个资源时, 可能有一些意外情况导致播放失败(如网络环境差). 此时当用户点击刷新按钮, 我们需要对当前的资源(Asset)进行刷新. SJVideoPlayer提供了直接的方法去刷新, 不需要开发者再重复的去创建新的Asset.
/// 以下为示例:
// 对当前资源进行刷新, 尝试重新播放视频
[_videoPlayer refresh];
- 记录某个播放位置. 我们有时候想存储某个视频的播放记录, 以便下次, 能够从指定的位置进行播放. 那什么时候存储合适呢? 最好的时机就是资源被释放时. SJVideoPlayer提供了每个资源在Dealloc中, 都进行的回调, 如下:
/// 以下为示例:
// 每个资源dealloc时的回调
_videoPlayer.assetDeallocExeBlock = ^(__kindof SJBaseVideoPlayer * _Nonnull videoPlayer) {
// .....
};
- 续播. 在播放时, 我们可能需要切换界面, 而希望视频能够在下一个界面无缝的进行播放. 针对此种情况 SJVideoPlayerURLAsset 提供了便利的初始化方法. 请看片段:
+ (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
优雅自如的旋转
对于旋转, 我们开发者肯定需要绝对的控制, 例如: 设置自动旋转所支持方向. 能够主动+自动旋转, 而且还需要能在适当的时候禁止自动旋转. 旋转前后的回调等等... 放心这些功能都有, 我挨个给大家介绍一下:
先说说何为自动旋转. 其实就是播放器根据当前设备的方向, 进行自动旋转.
- 设置自动旋转所支持方向, SJVideoPlayer自动旋转支持的方向如下:
/// 自动旋转所支持的方向
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]; // 主动旋转, 让播放器旋转到用户当前的设备方向或小屏.
- 旋转前后的回调. 我们在播放一个视频时, 小屏播的时候, 状态栏的style一般为UIStatusBarStyleDefault. 但是全屏播放视频时, 状态栏就得变成UIStatusBarStyleLightContent, 看下图对比:
- 白条.png
-
黑条.png
额, 我们赶紧说回调吧, 状态栏还是变成白的好一点. 旋转前的回调以及旋转后的回调如下:
/// 旋转前的回调
@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;
}
-
禁止自动旋转. 这个功能是必须有的, 如果不禁止旋转, 请看图:
需要禁止旋转.gif
SJVideoPlayer可以通过如下方式禁止自动旋转:
// 禁止自动旋转.
_videoPlayer.disableAutoRotation = YES;
这里有两点需要注意: 1. 返回时要记得恢复自动旋转. 2. 禁止自动旋转后, 手动点击全屏按钮, 还是可以旋转的.
-
禁止任何旋转. 也就是锁屏. 请看图:
锁屏.gif
请注意: 在锁屏状态下, 此时不管是主动旋转, 还是自动旋转, 都将不触发. 代码如下:
/// 锁屏
_videoPlayer.lockedScreen = YES;
- 还有一些其他便利的属性, 如下:
/// 是否是全屏
@property (nonatomic, readonly) BOOL isFullScreen;
/// 当前播放器的方向
@property (nonatomic) SJOrientation orientation;
/// 当前播放器旋转到的设备方向
@property (nonatomic, readonly) UIInterfaceOrientation currentOrientation;
播放的控制
SJVideoPlayer的常规播放控制大概有: 静音, 自动播放, 使播放, 使暂停, 使停止, 使重播.
哦, 对了还有亮度, 声音, 速率(rate)这些的设置. 并且都有相应的回调. 代码我就不贴了, 一看就明白了.
我再介绍一下其他的控制功能:
- 后台播放视频, 这个功能我引用自: https://juejin.im/post/5a38e1a0f265da4327185a26, 大家可以给点个❤️鼓励一下作者. 我将这个功能集成到了SJVideoPlayer播放器中, 如下:
/**
关于后台播放视频, 引用自: 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; // 请记得按上述注释的步骤配置
- 播放完毕的回调. 我们有时候希望能够重复的播放一个视频. 这时可能需要监听当前的视频有没有播放结束. SJVideoPlayer 提供了播放视频完毕后的回调, 代码如下:
__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
如果您有什么建议, 望请联系我!