# Player with default control layer.
pod 'SJVideoPlayer'
# The base player, without the control layer, can be used if you need a custom control layer.
pod 'SJBaseVideoPlayer'
- Quick initialization
- Support Fullscreen Pop Gesture
- Network status change prompt
- Support rotation to the orientation you want
- Export clips or generate GIF or Screenshot
- Custom control layer
- Support in TableHeaderView | TableViewCell | CollectionViewCell playing video
- Adjust brightness by slide vertical at left side of screen
- Adjust volume by slide vertical at right side of screen
- Slide horizontal to fast forward and rewind
- Full screen mode drag will display video preview
- Continue playing, Jumping into the next interface can use the resource initialization of the previous interface
- Email: changsanjiang@gmail.com
- QQGroup: 719616775
SJVideoPlayer is available under the MIT license. See the LICENSE file for more info.
/// 以下为示例:
_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;
}
- 资源刷新. 在播放一个资源时, 可能有一些意外情况导致播放失败(如网络环境差). 此时当用户点击刷新按钮, 我们需要对当前的资源(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, 其他方面同开始的示例一模一样.
对于旋转, 我们开发者肯定需要绝对的控制, 例如: 设置自动旋转所支持方向. 能够主动+自动旋转, 而且还需要能在适当的时候禁止自动旋转. 旋转前后的回调等等... 放心这些功能都有, 我挨个给大家介绍一下:
先说说何为自动旋转. 其实就是播放器根据当前设备的方向, 进行自动旋转.
- 设置自动旋转所支持方向, 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, 看下图对比:
/// 旋转前的回调
@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, 大家可以给点个❤️鼓励一下作者. 我将这个功能集成到了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 方法, 让其从头重新开始播放.
有时候我们需要能够友好的告诉客户当前的网络状态发生了改变, 毕竟流量是要钱的. 我们继续看图: 这些提示, 我都做了本地化处理, 支持的语言有: 中文/繁体/英文. 开发者也可以自己定义想要的提示. 后面我会介绍SJVideoPlayer全局的配置类, 它可以配置各个控件的图片, slider, 本地化的一些提示等等.
介绍:
使用: