功能齊全的視頻播放器-SJVideoPlayer基本介紹和使用

Installation

# Player with default control layer.
pod 'SJVideoPlayer'
# The base player, without the control layer, can be used if you need a custom control layer. 需要自己定義播放UI時(shí)使用
pod 'SJBaseVideoPlayer'

天朝

# 如果網(wǎng)絡(luò)不行安裝不了, 可改成以下方式進(jìn)行安裝
pod 'SJBaseVideoPlayer', :git => 'https://gitee.com/changsanjiang/SJBaseVideoPlayer.git'
pod 'SJVideoPlayer', :git => 'https://gitee.com/changsanjiang/SJVideoPlayer.git'
pod 'SJUIKit/AttributesFactory', :git => 'https://gitee.com/changsanjiang/SJUIKit.git'
pod 'SJUIKit/ObserverHelper', :git => 'https://gitee.com/changsanjiang/SJUIKit.git'
pod 'SJUIKit/Queues', :git => 'https://gitee.com/changsanjiang/SJUIKit.git'
$ pod update --no-repo-update   (不要用 pod install 了, 用這個(gè)命令安裝)

Example 使用示例

_player = [SJVideoPlayer player];
_player.view.frame = CGRectMake(0, 0, 200, 200);
[self.view addSubview:_player.view];
// 設(shè)置資源進(jìn)行播放
_player.URLAsset = [[SJVideoPlayerURLAsset alloc] initWithURL:URL];
... 等等, 更多設(shè)置, 請查看頭文件. 相應(yīng)功能均為懶加載, 用到時(shí)才會創(chuàng)建. 

Documents

1. 視圖層次結(jié)構(gòu)
2. URLAsset
3. 播放控制
4. 控制層的顯示和隱藏
5. 設(shè)備亮度和音量
6. 旋轉(zhuǎn)
7. 直接全屏而不旋轉(zhuǎn)
8. 鏡像翻轉(zhuǎn)
9. 網(wǎng)絡(luò)狀態(tài)
10. 手勢
11. 占位圖
12. 顯示提示文本
13. 一些固定代碼
14. 截屏
15. 導(dǎo)出視頻或GIF
16. 滾動相關(guān)
17. 自動播放 - 在 UICollectionView 或者 UITableView 中
18. 對控制層上的Item的操作
19. 對控制層上的Item的一些補(bǔ)充
20. SJEdgeControlLayer 的補(bǔ)充

以下為詳細(xì)介紹:

1.1 UIView
1.1 UIView
_player = [SJVideoPlayer player];
_player.view.frame = ...;
[self.view addSubview:_player.view];
// 設(shè)置資源進(jìn)行播放
SJVideoPlayerURLAsset *asset = [[SJVideoPlayerURLAsset alloc] initWithURL:URL];
_player.URLAsset = asset;

在普通視圖中播放時(shí), 不需要指定視圖層次, 直接創(chuàng)建資源進(jìn)行播放即可.

1.2 UITableView 中的層次結(jié)構(gòu)

由于 UITableView 及 UICollectionView 的復(fù)用機(jī)制, 會導(dǎo)致播放器視圖顯示在錯誤的位置上, 為防止出現(xiàn)此種情況, 在創(chuàng)建資源時(shí)指定視圖層次結(jié)構(gòu), 使得播放器能夠定位具體的父視圖, 依此來控制隱藏與顯示.

1.2.1 UITableViewCell
--  UITableView
--  UITableViewCell
--  Player.superview
--  Player.view
_player = [SJVideoPlayer player];

UIView *playerSuperview = cell.coverImageView;
SJPlayModel *playModel = [SJPlayModel UITableViewCellPlayModelWithPlayerSuperviewTag:playerSuperview.tag atIndexPath:indexPath tableView:self.tableView];
_player.URLAsset = [[SJVideoPlayerURLAsset alloc] initWithURL:URL playModel:playModel];

在 UITableViewCell 中播放時(shí), 需指定 Cell 所處的 indexPath 以及播放器父視圖的 tag.
在滑動時(shí), 管理類將會通過這兩個(gè)參數(shù)控制播放器父視圖的顯示與隱藏.

1.2.2 UITableView.tableHeaderView
--  UITableView
--  UITableView.tableHeaderView 或者 UITableView.tableFooterView  
--  Player.superview
--  Player.view
UIView *playerSuperview = self.tableView.tableHeaderView;
// 也可以設(shè)置子視圖
// playerSuperview = self.tableView.tableHeaderView.coverImageView;
SJPlayModel *playModel = [SJPlayModel UITableViewHeaderViewPlayModelWithPlayerSuperview:playerSuperview tableView:self.tableView];
1.2.4 UITableViewHeaderFooterView
--  UITableView
--  UITableViewHeaderFooterView 
--  Player.superview
--  Player.view            
/// isHeader: 當(dāng)在header中播放時(shí), 傳YES, 在footer時(shí), 傳NO.
SJPlayModel *playModel = [SJPlayModel UITableViewHeaderFooterViewPlayModelWithPlayerSuperviewTag:sectionHeaderView.coverImageView.tag inSection:section isHeader:YES tableView:self.tableView];

1.3 UICollectionView 中的層次結(jié)構(gòu)

1.3.1 UICollectionViewCell
--  UICollectionView
--  UICollectionViewCell
--  Player.superview
--  Player.view
SJPlayModel *playModel = [SJPlayModel UICollectionViewCellPlayModelWithPlayerSuperviewTag:cell.coverImageView.tag atIndexPath:indexPath collectionView:self.collectionView];

1.4 嵌套時(shí)的視圖層次

嵌套的情況下, 傳遞的參數(shù)比較多, 不過熟悉了前面的套路, 下面的這些也不成問題. (會被復(fù)用的視圖, 傳 tag. 如果不會被復(fù)用, 則直接傳視圖)

1.4.1 UICollectionView 嵌套在 UITableViewCell 中
--  UITableView
--  UITableViewCell
--  UITableViewCell.UICollectionView
--  UICollectionViewCell
--  Player.superview
--  Player.view
SJPlayModel *playModel = [SJPlayModel UICollectionViewNestedInUITableViewCellPlayModelWithPlayerSuperviewTag:collectionViewCell.coverImageView.tag atIndexPath:collectionViewCellAtIndexPath collectionViewTag:tableViewCell.collectionView.tag collectionViewAtIndexPath:tableViewCellAtIndexPath tableView:self.tableView];
1.4.2 UICollectionView 嵌套在 UITableViewHeaderView 中
--  UITableView
--  UITableView.tableHeaderView 或者 UITableView.tableFooterView  
--  tableHeaderView.UICollectionView
--  UICollectionViewCell
--  Player.superview
--  Player.view
SJPlayModel *playModel = [SJPlayModel UICollectionViewNestedInUITableViewHeaderViewPlayModelWithPlayerSuperviewTag:cell.coverImageView.tag atIndexPath:indexPath collectionView:tableHeaderView.collectionView tableView:self.tableView];
1.4.3 UICollectionView 嵌套在 UICollectionViewCell 中
--  UICollectionView
--  UICollectionViewCell
--  UICollectionViewCell.UICollectionView
--  UICollectionViewCell
--  Player.superview
--  Player.view
SJPlayModel *playModel = [SJPlayModel UICollectionViewNestedInUICollectionViewCellPlayModelWithPlayerSuperviewTag:collectionViewCell.coverImageView.tag atIndexPath:collectionViewCellAtIndexPath collectionViewTag:rootCollectionViewCell.collectionView.tag collectionViewAtIndexPath:collectionViewAtIndexPath rootCollectionView:self.collectionView];

2. URLAsset

播放器 播放的資源是通過 SJVideoPlayerURLAsset 創(chuàng)建的. SJVideoPlayerURLAsset 由兩部分組成:
視圖層次 (第一部分中的SJPlayModel) 資源地址 (可以是本地資源/URL/AVAsset)
默認(rèn)情況下, 創(chuàng)建了 SJVideoPlayerURLAsset , 賦值給播放器后即可播放.

2.1 播放 URL(本地文件或遠(yuǎn)程資源)
NSURL *URL = [NSURL URLWithString:@"https://...example.mp4"];
_player.URLAsset = [[SJVideoPlayerURLAsset alloc] initWithURL:URL];

2.3 從指定的位置開始播放

NSTimeInterval secs = 20.0;
_player.URLAsset = [[SJVideoPlayerURLAsset alloc] initWithURL:URL specifyStartTime:secs]; // 直接從20秒處開始播放

2.4 續(xù)播(進(jìn)入下個(gè)頁面時(shí), 繼續(xù)播放)

我們可能需要切換界面時(shí), 希望視頻能夠在下一個(gè)界面無縫的進(jìn)行播放. 使用如下方法, 傳入正在播放的資源, 將新的資源賦值給播放器播放即可.

// otherAsset 即為上一個(gè)頁面播放的Asset
// 除了需要一個(gè)otherAsset, 其他方面同以上的示例一模一樣
_player.URLAsset = [SJVideoPlayerURLAsset.alloc initWithOtherAsset:otherAsset]; 

2.5 銷毀時(shí)的回調(diào). 可在此做一些記錄工作, 如播放記錄

// 每個(gè)資源dealloc時(shí)的回調(diào)
_player.assetDeallocExeBlock = ^(__kindof SJBaseVideoPlayer * _Nonnull videoPlayer) {
// .....
};

當(dāng)資源銷毀時(shí), 播放器將會回調(diào)該 block.

3. 播放控制

播放控制: 對播放進(jìn)行的操作. 此部分的內(nèi)容由 "id <SJMediaPlaybackController> playbackController" 提供支持.
大多數(shù)對播放進(jìn)行的操作, 均在協(xié)議 SJMediaPlaybackController 進(jìn)行了聲明.
正常來說實(shí)現(xiàn)了此協(xié)議的任何對象, 均可賦值給 player.playbackController 來替換原始實(shí)現(xiàn).

3.1 播放
[_player play];
3.2 暫停
[_player pause];
3.3 刷新

在播放一個(gè)資源時(shí), 可能有一些意外情況導(dǎo)致播放失敗(如網(wǎng)絡(luò)環(huán)境差).
此時(shí)當(dāng)用戶點(diǎn)擊刷新按鈕, 我們需要對當(dāng)前的資源(Asset)進(jìn)行刷新.
SJBaseVideoPlayer提供了直接的方法去刷新, 不需要開發(fā)者再重復(fù)的去創(chuàng)建新的Asset.

[_player refresh];
3.4 重播

從頭開始重新播放

[_player replay];
3.5 停止

停止播放, 請注意: 當(dāng)前資源將會被清空, 如需重播, 請重新設(shè)置新資源

[_player stop];
3.6 靜音
_player.muted = YES;
3.7 調(diào)速
// 默認(rèn)值為 1.0
_player.rate = 1.0;
3.8 報(bào)錯

當(dāng)播放發(fā)生錯誤時(shí), 可以通過它來獲取錯誤信息

_player.error
3.9 跳轉(zhuǎn)
///
/// 是否精確跳轉(zhuǎn), default value is NO.
///
@property (nonatomic) BOOL accurateSeeking;
///
/// 跳轉(zhuǎn)到指定位置播放
///
- (void)seekToTime:(NSTimeInterval)secs completionHandler:(void (^ __nullable)(BOOL finished))completionHandler;
- (void)seekToTime:(CMTime)time toleranceBefore:(CMTime)toleranceBefore toleranceAfter:(CMTime)toleranceAfter completionHandler:(void (^ __nullable)(BOOL finished))completionHandler;
3.10 切換清晰度
<pre style="box-sizing: border-box; overflow: auto; font-family: SFMono-Regular, Menlo, Monaco, Consolas, &quot;Liberation Mono&quot;, &quot;Courier New&quot;, monospace; font-size: 13.6px; margin-top: 0px; margin-bottom: 0px; overflow-wrap: normal; padding: 16px; line-height: 1.45; background-color: rgb(246, 248, 250); border-radius: 3px; word-break: normal; tab-size: 4; color: rgb(51, 51, 51);">///
/// 切換清晰度
///
- (void)switchVideoDefinition:(SJVideoPlayerURLAsset *)URLAsset;

///
/// 當(dāng)前清晰度切換的信息
///
@property (nonatomic, strong, readonly) SJVideoDefinitionSwitchingInfo *definitionSwitchingInfo;

/// 以下為設(shè)置 SJVideoPlayer.definitionURLAssets, 將會在清晰度切換控制層中顯示這些資源項(xiàng). 

SJVideoPlayerURLAsset *asset1 = [[SJVideoPlayerURLAsset alloc] initWithURL:VideoURL_Level4];
asset1.definition_fullName = @"超清 1080P";
asset1.definition_lastName = @"超清";

SJVideoPlayerURLAsset *asset2 = [[SJVideoPlayerURLAsset alloc] initWithURL:VideoURL_Level3];
asset2.definition_fullName = @"高清 720P";
asset2.definition_lastName = @"AAAAAAA";

SJVideoPlayerURLAsset *asset3 = [[SJVideoPlayerURLAsset alloc] initWithURL:VideoURL_Level2];
asset3.definition_fullName = @"清晰 480P";
asset3.definition_lastName = @"480P";
_player.definitionURLAssets = @[asset1, asset2, asset3];

// 先播放asset1\. (asset2 和 asset3 將會在用戶選擇后進(jìn)行切換)
_player.URLAsset = asset1;</pre>

### [](https://gitee.com/changsanjiang/SJVideoPlayer#311)
3.11 當(dāng)前時(shí)間
@property (nonatomic, readonly) NSTimeInterval currentTime;                         ///< 當(dāng)前播放到的時(shí)間
3.12 總時(shí)長
@property (nonatomic, readonly) NSTimeInterval duration;                            ///< 總時(shí)長
3.13 緩沖時(shí)長
@property (nonatomic, readonly) NSTimeInterval playableDuration;                    ///< 緩沖到的時(shí)間
3.14 是否已播放完畢
@property (nonatomic, readonly) BOOL isPlayedToEndTime;                             ///< 當(dāng)前資源是否已播放結(jié)束
3.15 是否調(diào)用過播放
@property (nonatomic, readonly) BOOL isPlayed;                                      ///< 是否播放過當(dāng)前的資源
3.16 是否調(diào)用過重播
@property (nonatomic, readonly) BOOL isReplayed;                                    ///< 是否重播過當(dāng)前的資源
3.17 設(shè)置新資源時(shí), 是否自動播放
@property (nonatomic) BOOL autoplayWhenSetNewAsset;                    ///< 設(shè)置新的資源后, 是否自動調(diào)用播放. 默認(rèn)為 YES
3.18 進(jìn)入后臺, 是否暫停播放

關(guān)于后臺播放視頻, 引用自: https://juejin.im/post/5a38e1a0f265da4327185a26
當(dāng)您想在后臺播放視頻時(shí):
1.需要設(shè)置 videoPlayer.pauseWhenAppDidEnterBackground = NO; (該值默認(rèn)為YES, 即App進(jìn)入后臺默認(rèn)暫停).
2.前往 TARGETS -> Capability -> enable Background Modes -> select this mode Audio, AirPlay, and Picture in Picture

_player.pauseWhenAppDidEnterBackground = NO; // 默認(rèn)值為 YES, 即進(jìn)入后臺后 暫停.
3.19 進(jìn)入前臺, 是否恢復(fù)播放
@property (nonatomic) BOOL resumePlaybackWhenAppDidEnterForeground;    ///< 進(jìn)入前臺時(shí), 是否恢復(fù)播放. 默認(rèn)為 NO
3.20 跳轉(zhuǎn)完成, 是否恢復(fù)播放
@property (nonatomic) BOOL resumePlaybackWhenPlayerHasFinishedSeeking; ///< 當(dāng)`seekToTime:`操作完成后, 是否恢復(fù)播放. 默認(rèn)為 YES
3.21 資源準(zhǔn)備狀態(tài)

資源準(zhǔn)備(或初始化)的狀態(tài)
當(dāng)未設(shè)置資源時(shí), 此時(shí) player.assetStatus = .unknown 當(dāng)設(shè)置新資源時(shí), 此時(shí) player.assetStatus = .preparing 當(dāng)準(zhǔn)備好播放時(shí), 此時(shí) player.assetStatus = .readyToPlay 當(dāng)初始化失敗時(shí), 此時(shí) player.assetStatus = .failed

typedef NS_ENUM(NSInteger, SJAssetStatus) {
///
/// 未知狀態(tài)
///
SJAssetStatusUnknown,

///
/// 準(zhǔn)備中
///
SJAssetStatusPreparing,

///
/// 當(dāng)前資源可隨時(shí)進(jìn)行播放(播放控制請查看`timeControlStatus`)
///
SJAssetStatusReadyToPlay,

///
/// 發(fā)生錯誤
///
SJAssetStatusFailed
};
3.22 播放控制狀態(tài)

暫?;虿シ诺目刂茽顟B(tài)
當(dāng)調(diào)用了暫停時(shí), 此時(shí) player.timeControlStatus = .paused
當(dāng)調(diào)用了播放時(shí), 此時(shí) 將可能處于以下兩種狀態(tài)中的任意一個(gè):
player.timeControlStatus = .playing 正在播放中.
player.timeControlStatus = .waitingToPlay 等待播放, 等待的原因請查看 player.reasonForWaitingToPlay

typedef NS_ENUM(NSInteger, SJPlaybackTimeControlStatus) {
///
/// 暫停狀態(tài)(已調(diào)用暫?;蛭磮?zhí)行任何操作的狀態(tài))
///
SJPlaybackTimeControlStatusPaused,

///
/// 播放狀態(tài)(已調(diào)用播放), 當(dāng)前正在緩沖或正在評估能否播放. 可以通過`reasonForWaitingToPlay`來獲取原因, UI層可以根據(jù)原因來控制loading視圖的狀態(tài).
///
SJPlaybackTimeControlStatusWaitingToPlay,

///
/// 播放狀態(tài)(已調(diào)用播放), 當(dāng)前播放器正在播放
///
SJPlaybackTimeControlStatusPlaying
};
3.23 播放等待的原因

當(dāng)調(diào)用了播放, 播放器未能播放處于等待狀態(tài)時(shí)的原因
等待原因有以下3種狀態(tài): 1.未設(shè)置資源, 此時(shí)設(shè)置資源后, 當(dāng)player.assetStatus = .readyToPlay, 播放器將自動進(jìn)行播放. 2.可能是由于緩沖不足, 播放器在等待緩存足夠時(shí)自動恢復(fù)播放, 此時(shí)可以顯示loading視圖. 3.可能是正在評估緩沖中, 這個(gè)過程會進(jìn)行的很快, 不需要顯示loading視圖.

///
/// 緩沖中, UI層建議顯示loading視圖 
///
extern SJWaitingReason const SJWaitingToMinimizeStallsReason;

///
/// 正在評估能否播放, 處于此狀態(tài)時(shí), 不建議UI層顯示loading視圖
///
extern SJWaitingReason const SJWaitingWhileEvaluatingBufferingRateReason;

///
/// 未設(shè)置資源
///
extern SJWaitingReason const SJWaitingWithNoAssetToPlayReason;
3.24 監(jiān)聽狀態(tài)改變??
///
/// 觀察者
///
///         可以如下設(shè)置block, 來監(jiān)聽某個(gè)狀態(tài)的改變
///         了解更多請前往頭文件查看
///         player.playbackObserver.currentTimeDidChangeExeBlock = ...;
///         player.playbackObserver.durationDidChangeExeBlock = ...;
///         player.playbackObserver.timeControlStatusDidChangeExeBlock = ...;
///
@property (nonatomic, strong, readonly) SJPlaybackObservation *playbackObserver;
3.25 已觀看的時(shí)長(當(dāng)前資源)
@property (nonatomic, readonly) NSTimeInterval durationWatched;                     ///< 已觀看的時(shí)長(當(dāng)前資源)
3.26 接入別的視頻 SDK, 自己動手?jǐn)]一個(gè) SJVideoPlayerPlaybackController, 替換作者原始實(shí)現(xiàn)

某些時(shí)候, 我們需要接入第三方的視頻SDK, 但是又想使用 SJBaseVideoPlayer 封裝的其他的功能.
這個(gè)時(shí)候, 我們可以自己動手, 將第三方的SDK封裝一下, 實(shí)現(xiàn) SJVideoPlayerPlaybackController 協(xié)議, 管理 SJBaseVideoPlayer 中的播放操作.
示例:

_player.playbackController = Your PlaybackController.

4. 控制層的顯示和隱藏

控制層的顯示和隱藏, 此部分的內(nèi)容由 "id <SJControlLayerAppearManager> controlLayerAppearManager" 提供支持.
controlLayerAppearManager 內(nèi)部存在一個(gè)定時(shí)器, 當(dāng)控制層顯示時(shí), 會開啟此定時(shí)器. 一定間隔后, 會嘗試隱藏控制層.
其他相關(guān)操作, 請見以下內(nèi)容.

4.1 讓控制層顯示

當(dāng)控制層需要顯示時(shí), 可以調(diào)用下面方法.

[_player controlLayerNeedAppear];

此方法將會回調(diào)控制層的代理方法:
"- (void)controlLayerNeedAppear:(__kindof SJBaseVideoPlayer *)videoPlayer;"
代理將會對當(dāng)前的控制層進(jìn)行顯示處理.

4.2 讓控制層隱藏

當(dāng)控制層需要隱藏時(shí), 可以調(diào)用下面方法.

[_player controlLayerNeedDisappear];

此方法將會回調(diào)控制層的代理方法:
"- (void)controlLayerNeedDisappear:(__kindof SJBaseVideoPlayer *)videoPlayer;"
代理將會對當(dāng)前的控制層進(jìn)行隱藏處理.

4.3 控制層是否顯示中
///
/// 控制層的顯示狀態(tài)(是否已顯示)
///
@property (nonatomic, getter=isControlLayerAppeared) BOOL controlLayerAppeared;
4.4 是否在暫停時(shí)保持控制層顯示
///
/// 暫停的時(shí)候是否保持控制層顯示
///
///         default value is NO
///
@property (nonatomic) BOOL pausedToKeepAppearState;
4.5 監(jiān)聽狀態(tài)改變??
///
/// 觀察者
///
///         當(dāng)需要監(jiān)聽控制層的顯示和隱藏時(shí), 可以設(shè)置`player.controlLayerAppearObserver.appearStateDidChangeExeBlock = ...;`
///
@property (nonatomic, strong, readonly) id<SJControlLayerAppearManagerObserver> controlLayerAppearObserver;
4.6 自己動手?jǐn)]一個(gè) SJControlLayerAppearManager, 替換作者原始實(shí)現(xiàn)

同樣的, 協(xié)議 "SJControlLayerAppearManager" 定義了一系列的操作, 只要實(shí)現(xiàn)了這些協(xié)議方法的對象, 就可以管理控制層的顯示和隱藏.

_player.controlLayerAppearManager = Your controlLayerAppearManager; 

5. 設(shè)備亮度和音量

設(shè)備亮度和音量的調(diào)整, 此部分的內(nèi)容由 "id <SJDeviceVolumeAndBrightnessManager> deviceVolumeAndBrightnessManager" 提供支持.

5.1 調(diào)整設(shè)備亮度
// 0 到 1
_player.deviceVolumeAndBrightnessManager.brightness = 1.0;

5.2 調(diào)整設(shè)備聲音

// 0 到 1
_player.deviceVolumeAndBrightnessManager.volume = 1.0;
5.3 監(jiān)聽狀態(tài)改變??
///
/// 觀察者
///
@property (nonatomic, strong, readonly) id<SJDeviceVolumeAndBrightnessManagerObserver> deviceVolumeAndBrightnessObserver;
5.4 禁止播放器設(shè)置
_player.disableBrightnessSetting = YES;
_player.disableVolumeSetting = YES;
5.5 自己動手?jǐn)]一個(gè) SJDeviceVolumeAndBrightnessManager, 替換作者原始實(shí)現(xiàn)

當(dāng)需要對設(shè)備音量視圖進(jìn)行自定義時(shí), 可以自己動手?jǐn)]一個(gè) SJDeviceVolumeAndBrightnessManager.

_player.deviceVolumeAndBrightnessManager = Your deviceVolumeAndBrightnessManager;

6. 旋轉(zhuǎn)

此部分的內(nèi)容由 "id <SJRotationManagerProtocol> rotationManager" 提供支持.
對于旋轉(zhuǎn), 我們開發(fā)者肯定需要絕對的控制, 例如: 設(shè)置自動旋轉(zhuǎn)所支持方向. 能夠主動+自動旋轉(zhuǎn), 而且還需要能在適當(dāng)?shù)臅r(shí)候禁止自動旋轉(zhuǎn). 旋轉(zhuǎn)前后的回調(diào)等等... 放心這些功能都有, 我挨個(gè)給大家介紹.
另外旋轉(zhuǎn)有兩種方式:
僅旋轉(zhuǎn)播放器視圖 (默認(rèn)情況下)
使 ViewController 也一起旋轉(zhuǎn)
具體請看下面介紹.

6.1 自動旋轉(zhuǎn)

先說說何為自動旋轉(zhuǎn). 其實(shí)就是當(dāng)設(shè)備方向變更時(shí), 播放器根據(jù)設(shè)備方向進(jìn)行自動旋轉(zhuǎn).

6.2 設(shè)置自動旋轉(zhuǎn)支持的方向
/// 設(shè)置自動旋轉(zhuǎn)支持的方向
_player.rotationManager.autorotationSupportedOrientations = SJOrientationMaskLandscapeLeft | SJOrientationMaskLandscapeRight;
/**
自動旋轉(zhuǎn)支持的方向
- SJOrientationMaskPortrait:       豎屏
- SJOrientationMaskLandscapeLeft:  支持全屏, Home鍵在右側(cè)
- SJOrientationMaskLandscapeRight: 支持全屏, Home鍵在左側(cè)
- SJOrientationMaskAll:            全部方向
*/
typedef enum : NSUInteger {
SJOrientationMaskPortrait = 1 << SJOrientation_Portrait,
SJOrientationMaskLandscapeLeft = 1 << SJOrientation_LandscapeLeft,
SJOrientationMaskLandscapeRight = 1 << SJOrientation_LandscapeRight,
SJOrientationMaskAll = SJOrientationMaskPortrait | SJOrientationMaskLandscapeLeft | SJOrientationMaskLandscapeRight,
} SJOrientationMask;
6.3 禁止自動旋轉(zhuǎn)

這里有兩點(diǎn)需要注意:
合適的時(shí)候要記得恢復(fù)自動旋轉(zhuǎn).
禁止自動旋轉(zhuǎn)后, 主動調(diào)用旋轉(zhuǎn), 還是可以旋轉(zhuǎn)的.

_player.rotationManager.disabledAutorotation = YES;
6.4 主動調(diào)用旋轉(zhuǎn)

主動旋轉(zhuǎn). 當(dāng)我們想主動旋轉(zhuǎn)時(shí), 大概分為以下三點(diǎn):

播放器旋轉(zhuǎn)到用戶當(dāng)前的設(shè)備方向或恢復(fù)小屏.
主動旋轉(zhuǎn)到指定方向.
主動旋轉(zhuǎn)完成后的回調(diào).
請看以下方法, 分別對應(yīng)以上三點(diǎn):

- (void)rotate;
- (void)rotate:(SJOrientation)orientation animated:(BOOL)animated;
- (void)rotate:(SJOrientation)orientation animated:(BOOL)animated completion:(void (^ _Nullable)(__kindof SJBaseVideoPlayer *player))block;
6.5 是否全屏
/// 如果為YES, 表示全屏
@property (nonatomic, readonly) BOOL isFullScreen;                              ///< 是否已全屏
6.6 是否正在旋轉(zhuǎn)
/// 如果為YES, 表示正在旋轉(zhuǎn)中
@property (nonatomic, readonly) BOOL isTransitioning;
6.7 當(dāng)前旋轉(zhuǎn)的方向
_player.rotationManager.currentOrientation
6.8 監(jiān)聽狀態(tài)改變??
///
/// 觀察者
///
/// 當(dāng)需要監(jiān)聽旋轉(zhuǎn)時(shí), 可以設(shè)置`player.rotationObserver.rotationDidStartExeBlock = ...;`
/// 了解更多請前往頭文件查看
///
@property (nonatomic, strong, readonly) id<SJRotationManagerObserver> rotationObserver;
6.9 自己動手?jǐn)]一個(gè) SJRotationManager, 替換作者原始實(shí)現(xiàn)

當(dāng)你想替換原始實(shí)現(xiàn)時(shí), 可以實(shí)現(xiàn) SJRotationManagerProtocol 中定義的方法.

7. 直接全屏而不旋轉(zhuǎn)

直接全屏, 或者說充滿屏幕, 但不旋轉(zhuǎn).

7.1 全屏和恢復(fù)
_player.fitOnScreen = YES;

[_player setFitOnScreen:NO animated:NO];

[_player setFitOnScreen:YES animated:YES completionHandler:^(__kindof SJBaseVideoPlayer * _Nonnull player) {
/// ...
}];
7.2 監(jiān)聽狀態(tài)改變??
@property (nonatomic, copy, nullable) void(^fitOnScreenWillBeginExeBlock)(__kindof SJBaseVideoPlayer *player);
@property (nonatomic, copy, nullable) void(^fitOnScreenDidEndExeBlock)(__kindof SJBaseVideoPlayer *player);;
7.3 是否是全屏
/// YES 為充滿屏幕 
_player.isFitOnScreen
7.4 自己動手?jǐn)]一個(gè) SJFitOnScreenManager, 替換作者原始實(shí)現(xiàn)

該部分管理類的協(xié)議定義在 SJFitOnScreenManagerProtocol 中, 實(shí)現(xiàn)該協(xié)議的任何對象, 均可賦值給播放器, 替換原始實(shí)現(xiàn).

8. 鏡像翻轉(zhuǎn)

此部分內(nèi)容由 id<SJFlipTransitionManager> flipTransitionManager 提供支持
目前鏡像翻轉(zhuǎn)只寫了 水平翻轉(zhuǎn), 未來可能會加入更多的翻轉(zhuǎn)類型.

typedef enum : NSUInteger {
SJViewFlipTransition_Identity,
SJViewFlipTransition_Horizontally, // 水平翻轉(zhuǎn)
} SJViewFlipTransition;
8.1 翻轉(zhuǎn)和恢復(fù)
/// 當(dāng)前的翻轉(zhuǎn)類型
_player.flipTransition
/// 翻轉(zhuǎn)相關(guān)方法
[_player setFlipTransition:SJViewFlipTransition_Horizontally];
[_player setFlipTransition:SJViewFlipTransition_Horizontally animated:YES];
[_player setFlipTransition:SJViewFlipTransition_Identity animated:YES completionHandler:^(__kindof SJBaseVideoPlayer * _Nonnull player) {
/// ...
}];
8.2 監(jiān)聽狀態(tài)改變??
///
/// 觀察者
///
///         可以如下設(shè)置block, 來監(jiān)聽某個(gè)狀態(tài)的改變
///
///         player.flipTransitionObserver.flipTransitionDidStartExeBlock = ...;
///         player.flipTransitionObserver.flipTransitionDidStopExeBlock = ...;
///
@property (nonatomic, strong, readonly) id<SJFlipTransitionManagerObserver> flipTransitionObserver;
8.3 自己動手?jǐn)]一個(gè) SJFlipTransitionManager, 替換作者原始實(shí)現(xiàn)

該部分管理類的協(xié)議定義在 SJFlipTransitionManagerProtocol 中, 實(shí)現(xiàn)該協(xié)議的任何對象, 均可賦值給播放器, 替換原始實(shí)現(xiàn).

9. 網(wǎng)絡(luò)狀態(tài)

此部分內(nèi)容由 id<SJReachability> reachability 提供支持
默認(rèn)的 reachability 是個(gè)單例, 在App生命周期中, 僅創(chuàng)建一次. 因此每個(gè)播放器對象持有的 reachability 都是相同的.

9.1 當(dāng)前的網(wǎng)絡(luò)狀態(tài)
@property (nonatomic, readonly) SJNetworkStatus networkStatus;
9.2 監(jiān)聽狀態(tài)改變??
///
/// 觀察者
///
@property (nonatomic, strong, readonly) id<SJReachabilityObserver> reachabilityObserver;
9.3 自己動手?jǐn)]一個(gè) SJReachability, 替換作者原始實(shí)現(xiàn)

該部分管理類的協(xié)議定義在 SJNetworkStatus 中, 實(shí)現(xiàn)該協(xié)議的任何對象, 均可賦值給播放器, 替換原始實(shí)現(xiàn).

10. 手勢

此部分內(nèi)容由 id<SJPlayerGestureControl> gestureControl 提供支持
播放器默認(rèn)存在四種手勢, 每個(gè)手勢觸發(fā)的回調(diào)均定義在 SJPlayerGestureControl 中, 當(dāng)想改變某個(gè)手勢的處理時(shí), 可以直接修改對應(yīng)手勢觸發(fā)的 block 即可.
具體請看以下部分.

10.1 單擊手勢

當(dāng)用戶單擊播放器時(shí), 播放器會調(diào)用 顯示或隱藏控制層的操作
以下為默認(rèn)實(shí)現(xiàn):

__weak typeof(self) _self = self;
_gestureControl.singleTapHandler = ^(id<SJPlayerGestureControl>  _Nonnull control, CGPoint location) {
__strong typeof(_self) self = _self;
if ( !self ) return ;
/// 讓控制層顯示或隱藏
[self.controlLayerAppearManager switchAppearState];
};
10.2 雙擊手勢

雙擊會觸發(fā)暫?;虿シ诺牟僮?/p>

__weak typeof(self) _self = self;
_gestureControl.doubleTapHandler = ^(id<SJPlayerGestureControl>  _Nonnull control, CGPoint location) {
__strong typeof(_self) self = _self;
if ( !self ) return ;
if ( [self playStatus_isPlaying] )
[self pause];
else
[self play];
};
10.3 移動手勢
  • 垂直滑動時(shí), 默認(rèn)情況下如果在屏幕左邊, 則會觸發(fā)調(diào)整亮度的操作, 并顯示亮度提示視圖. 如果在屏幕右邊, 則會觸發(fā)調(diào)整聲音的操作, 并顯示系統(tǒng)音量提示視圖
  • 水平滑動時(shí), 會觸發(fā)控制層相應(yīng)的代理方法
__weak typeof(self) _self = self;
_gestureControl.panHandler = ^(id<SJPlayerGestureControl>  _Nonnull control, SJPanGestureTriggeredPosition position, SJPanGestureMovingDirection direction, SJPanGestureRecognizerState state, CGPoint translate) {
__strong typeof(_self) self = _self;
if ( !self ) return ;
/// ....
};
10.4 捏合手勢

當(dāng)用戶做放大或收縮觸發(fā)該手勢時(shí), 會設(shè)置播放器顯示模式AspectAspectFill.

__weak typeof(self) _self = self;
_gestureControl.pinchHandler = ^(id<SJPlayerGestureControl>  _Nonnull control, CGFloat scale) {
__strong typeof(_self) self = _self;
if ( !self ) return ;
self.playbackController.videoGravity = scale > 1 ?AVLayerVideoGravityResizeAspectFill:AVLayerVideoGravityResizeAspect;
};
10.5 設(shè)置支持的手勢
_player.gestureControl.supportedGestureTypes = SJPlayerGestureTypeMask_All
typedef enum : NSUInteger {
SJPlayerGestureTypeMask_None,
SJPlayerGestureTypeMask_SingleTap = 1 << 0,
SJPlayerGestureTypeMask_DoubleTap = 1 << 1,
SJPlayerGestureTypeMask_Pan_H = 1 << 2, // 水平方向
SJPlayerGestureTypeMask_Pan_V = 1 << 3, // 垂直方向
SJPlayerGestureTypeMask_Pinch = 1 << 4,
SJPlayerGestureTypeMask_Pan = SJPlayerGestureTypeMask_Pan_H | SJPlayerGestureTypeMask_Pan_V,
SJPlayerGestureTypeMask_All = SJPlayerGestureTypeMask_SingleTap |
SJPlayerGestureTypeMask_DoubleTap |
SJPlayerGestureTypeMask_Pan |
SJPlayerGestureTypeMask_Pinch,
} SJPlayerGestureTypeMask;
10.6 自定義某個(gè)手勢的處理
/// 例如 替換單擊手勢的處理
__weak typeof(self) _self = self;
_player.gestureControl.singleTapHandler = ^(id<SJPlayerGestureControl>  _Nonnull control, CGPoint location) {
__strong typeof(_self) self = _self;
if ( !self ) return ;
/// .....你的處理
};

11. 占位圖

資源在初始化時(shí), 由于暫時(shí)沒有畫面可以呈現(xiàn), 會出現(xiàn)短暫的黑屏. 在此期間, 建議大家設(shè)置一下占位圖.

11.1 設(shè)置本地占位圖
_player.presentView.placeholderImageView.image = [UIImage imageNamed:@"..."];
11.2 設(shè)置網(wǎng)絡(luò)占位圖
[_player.presentView.placeholderImageView sd_setImageWithURL:URL placeholderImage:img];
11.3 是否隱藏占位圖 - 播放器準(zhǔn)備好顯示時(shí)
/// 播放器準(zhǔn)備好顯示時(shí), 是否隱藏占位圖
/// - 默認(rèn)為YES
@property (nonatomic) BOOL hiddenPlaceholderImageViewWhenPlayerIsReadyForDisplay;

12. 顯示提示文本

目前僅支持 NSAttributedString.

12.1 顯示管理類
///
/// 中心彈出文本提示
///
///         了解更多請前往協(xié)議頭文件查看
///
@property (nonatomic, strong, null_resettable) id<SJPromptProtocol> prompt;

///
/// 右下角彈出提示
///
///         了解更多請前往協(xié)議頭文件查看
///
@property (nonatomic, strong, null_resettable) id<SJPopPromptControllerProtocol> popPromptController;
12.2 配置提示文本
_player.prompt.backgroundColor = ...;
_player.prompt.contentInset = ...;

13. 一些固定代碼

接入播放器的 ViewController 中, 會寫一些固定的代碼, 我將這些固定代碼(例如 進(jìn)入下個(gè)頁面時(shí), 需要當(dāng)前頁面的播放器暫停), 都封裝在了以下方法中.

- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[_player vc_viewDidAppear];
}

在適當(dāng)?shù)臅r(shí)候直接調(diào)用即可, 以下為內(nèi)部實(shí)現(xiàn):

13.1 - (void)vc_viewDidAppear;

當(dāng) ViewController 的 viewDidAppear 調(diào)用時(shí), 恢復(fù)播放
實(shí)現(xiàn)如下:

- (void)vc_viewDidAppear {
if ( !self.isPlayOnScrollView || (self.isPlayOnScrollView && self.isScrollAppeared) ) {
/// 恢復(fù)播放
[self play];
}
/// 標(biāo)識vc已顯示 
/// vc_isDisappeared 是自動旋轉(zhuǎn)觸發(fā)的條件之一, 如果控制器 disappear 了, 就不會觸發(fā)旋轉(zhuǎn) 
self.vc_isDisappeared = NO;
}
13.2 - (void)vc_viewWillDisappear;

當(dāng) ViewController 的 viewWillDisappear 調(diào)用時(shí), 設(shè)置標(biāo)識為YES
實(shí)現(xiàn)如下:

- (void)vc_viewWillDisappear {
/// 標(biāo)識vc已顯示 
/// vc_isDisappeared 是自動旋轉(zhuǎn)觸發(fā)的條件之一, 如果控制器 disappear 了, 就不會觸發(fā)旋轉(zhuǎn) 
self.vc_isDisappeared = YES;
}
13.3 - (void)vc_viewDidDisappear;

當(dāng) ViewController 的 viewDidDisappear 調(diào)用時(shí), 暫停播放
實(shí)現(xiàn)如下:

- (void)vc_viewDidDisappear {
[self pause];
}
13.4 - (BOOL)vc_prefersStatusBarHidden;

狀態(tài)欄是否可以隱藏
實(shí)現(xiàn)如下:

- (BOOL)vc_prefersStatusBarHidden {
if ( _tmpShowStatusBar ) return NO;         // 臨時(shí)顯示
if ( _tmpHiddenStatusBar ) return YES;      // 臨時(shí)隱藏
if ( self.lockedScreen ) return YES;        // 鎖屏?xí)r, 不顯示
if ( self.rotationManager.isTransitioning ) { // 旋轉(zhuǎn)時(shí), 不顯示
if ( !self.disabledControlLayerAppearManager && self.isControlLayerAppeared ) return NO;
return YES;
}
// 全屏播放時(shí), 使?fàn)顟B(tài)欄根據(jù)控制層顯示或隱藏
if ( self.isFullScreen ) return !self.isControlLayerAppeared;
return NO;
}
13.5 - (UIStatusBarStyle)vc_preferredStatusBarStyle;

狀態(tài)欄顯示白色還是黑色
實(shí)現(xiàn)如下:

- (UIStatusBarStyle)vc_preferredStatusBarStyle {
// 全屏播放時(shí), 使?fàn)顟B(tài)欄變成白色
if ( self.isFullScreen || self.fitOnScreen ) return UIStatusBarStyleLightContent;
return UIStatusBarStyleDefault;
}
13.6 - 臨時(shí)顯示狀態(tài)欄

有時(shí)候, 可能會希望臨時(shí)顯示狀態(tài)欄, 例如全屏轉(zhuǎn)回小屏?xí)r, 旋轉(zhuǎn)之前, 需要將狀態(tài)欄顯示.

[_player needShowStatusBar]; 
13.7 - 臨時(shí)隱藏狀態(tài)欄

有時(shí)候, 可能會希望臨時(shí)隱藏狀態(tài)欄, 例如某個(gè)播放器控制層不需要顯示狀態(tài)欄.

[_player needHiddenStatusBar]; 

14. 截屏

14.1 當(dāng)前時(shí)間截圖
UIImage *img = [_player screenshot];
14.2 指定時(shí)間截圖
- (void)screenshotWithTime:(NSTimeInterval)secs
completion:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, UIImage * __nullable image, NSError *__nullable error))block;

/// 可以通過 _player.playbackController.presentationSize 來獲取當(dāng)前視頻寬高
- (void)screenshotWithTime:(NSTimeInterval)secs
size:(CGSize)size
completion:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, UIImage * __nullable image, NSError *__nullable error))block;

15. 導(dǎo)出視頻或GIF

15.1 導(dǎo)出視頻
- (void)exportWithBeginTime:(NSTimeInterval)beginTime
duration:(NSTimeInterval)duration
presetName:(nullable NSString *)presetName
progress:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, float progress))progressBlock
completion:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, NSURL *fileURL, UIImage *thumbnailImage))completion
failure:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, NSError *error))failure;
15.2 導(dǎo)出GIF
- (void)generateGIFWithBeginTime:(NSTimeInterval)beginTime
duration:(NSTimeInterval)duration
progress:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, float progress))progressBlock
completion:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, UIImage *imageGIF, UIImage *thumbnailImage, NSURL *filePath))completion
failure:(void(^)(__kindof SJBaseVideoPlayer *videoPlayer, NSError *error))failure;
15.3 取消操作
/// 取消導(dǎo)出操作
/// 播放器 dealloc 時(shí), 會調(diào)用一次 
- (void)cancelExportOperation;

/// 取消GIF操作
/// 播放器 dealloc 時(shí), 會調(diào)用一次 
- (void)cancelGenerateGIFOperation;

16. 滾動相關(guān)

此部分的內(nèi)容由 SJPlayModelPropertiesObserver 提供支持.

16.1 是否在 UICollectionView 或者 UITableView 中播放
/// 是否是在 UICollectionView 或者 UITableView 中播放
_player.isPlayOnScrollView
16.2 是否已顯示
///
/// 播放器視圖是否顯示
///
/// Whether the player is appeared when playing on scrollView. Because scrollview may be scrolled.
///
@property (nonatomic, readonly) BOOL isScrollAppeared;
16.3 播放器視圖將要滾動顯示和消失的回調(diào)
@property (nonatomic, copy, nullable) void(^playerViewWillAppearExeBlock)(__kindof SJBaseVideoPlayer *videoPlayer);
@property (nonatomic, copy, nullable) void(^playerViewWillDisappearExeBlock)(__kindof SJBaseVideoPlayer *videoPlayer);
16.4 滾動出去后, 是否暫停
///
/// 滾動出去后, 是否暫停. 默認(rèn)為YES
///
/// - default value is YES.
///
@property (nonatomic) BOOL pauseWhenScrollDisappeared;
16.5 滾動進(jìn)入時(shí), 是否恢復(fù)播放
///
/// 滾動進(jìn)入時(shí), 是否恢復(fù)播放. 默認(rèn)為YES
///
/// - default values is YES.
///
@property (nonatomic) BOOL resumePlaybackWhenScrollAppeared;
16.6 滾動出去后, 是否隱藏播放器視圖
///
/// 滾動出去后, 是否隱藏播放器視圖. 默認(rèn)為YES
///
/// - default value is YES.
///
@property (nonatomic) BOOL hiddenViewWhenScrollDisappeared;

17. 自動播放 - 在 UICollectionView 或者 UITableView 中

目前支持在 UICollectionViewCell 和 UITableViewCell 中自動播放.
使用之前, 請導(dǎo)入頭文件 #import "UIScrollView+ListViewAutoplaySJAdd.h"

17.1 開啟
/// 配置列表自動播放
[_tableView sj_enableAutoplayWithConfig:[SJPlayerAutoplayConfig configWithPlayerSuperviewTag:101 autoplayDelegate:self]];
/// Delegate method
- (void)sj_playerNeedPlayNewAssetAtIndexPath:(NSIndexPath *)indexPath {
}
17.2 配置
typedef NS_ENUM(NSUInteger, SJAutoplayScrollAnimationType) {
SJAutoplayScrollAnimationTypeNone,
SJAutoplayScrollAnimationTypeTop,
SJAutoplayScrollAnimationTypeMiddle,
};

@interface SJPlayerAutoplayConfig : NSObject
+ (instancetype)configWithPlayerSuperviewTag:(NSInteger)playerSuperviewTag
autoplayDelegate:(id<SJPlayerAutoplayDelegate>)autoplayDelegate;

/// 滾動的動畫類型
/// default is .Middle;
@property (nonatomic) SJAutoplayScrollAnimationType animationType;

@property (nonatomic, readonly) NSInteger playerSuperviewTag;
@property (nonatomic, weak, nullable, readonly) id<SJPlayerAutoplayDelegate> autoplayDelegate;
@end

@protocol SJPlayerAutoplayDelegate <NSObject>
- (void)sj_playerNeedPlayNewAssetAtIndexPath:(NSIndexPath *)indexPath;
@end
17.3 關(guān)閉
[_tableView sj_disenableAutoplay];
17.4 主動調(diào)用播放下一個(gè)資源
[_tableView sj_needPlayNextAsset];

18. 對控制層上面的Item的操作

18.1 添加
SJEdgeControlButtonItem *item = [[SJEdgeControlButtonItem alloc] initWithImage:[UIImage imageNamed:@"test"] target:self action:@selector(test) tag:SJTestImageItemTag];
[_player.defaultEdgeControlLayer.topAdapter addItem:item];
[_player.defaultEdgeControlLayer.topAdapter reload];

18.2 刪除
[_player.defaultEdgeControlLayer.bottomAdapter removeItemForTag:SJEdgeControlLayerBottomItem_Separator];
[_player.defaultEdgeControlLayer.bottomAdapter reload];
18.3 調(diào)整位置
[_player.defaultEdgeControlLayer.bottomAdapter exchangeItemForTag:SJEdgeControlLayerBottomItem_DurationTime withItemForTag:SJEdgeControlLayerBottomItem_Progress];
[_player.defaultEdgeControlLayer.bottomAdapter reload];

19. 對控制層上的Item的一些補(bǔ)充

19.1 設(shè)置與前后item的間距
SJEdgeControlButtonItem *titleItem = [_player.defaultEdgeControlLayer.topAdapter itemForTag:SJEdgeControlLayerTopItem_Title];
titleItem.insets = SJEdgeInsetsMake(16, 16);
[_player.defaultEdgeControlLayer.topAdapter reload];
19.2 設(shè)置隱藏
SJEdgeControlButtonItem *titleItem = [_player.defaultEdgeControlLayer.topAdapter itemForTag:SJEdgeControlLayerTopItem_Title];
titleItem.hidden = YES;
[_player.defaultEdgeControlLayer.topAdapter reload];
19.3 填充剩余空間
SJEdgeControlButtonItem *titleItem = [_player.defaultEdgeControlLayer.topAdapter itemForTag:SJEdgeControlLayerTopItem_Title];
titleItem.fill = YES;
[_player.defaultEdgeControlLayer.topAdapter reload];

20. SJEdgeControlLayer 的補(bǔ)充

20.1 是否豎屏?xí)r隱藏返回按鈕
_player.defaultEdgeControlLayer.hiddenBackButtonWhenOrientationIsPortrait = YES;
20.2 是否禁止網(wǎng)絡(luò)狀態(tài)變化提示
_player.defaultEdgeControlLayer.disabledPromptWhenNetworkStatusChanges = YES;
20.3 是否使返回按鈕常駐
_player.defaultEdgeControlLayer.showResidentBackButton = YES;
20.4 是否隱藏底部進(jìn)度條
_player.defaultEdgeControlLayer.hiddenBottomProgressIndicator = YES;
20.5 是否在loadingView上顯示網(wǎng)速
_player.defaultEdgeControlLayer.showNetworkSpeedToLoadingView = YES;
20.6 自定義loadingView
// 實(shí)現(xiàn)協(xié)議`SJEdgeControlLayerLoadingViewProtocol`即可, 然后賦值給控制層
_player.defaultEdgeControlLayer.loadingView = Your Loading View;
20.7 調(diào)整邊距
_player.defaultEdgeControlLayer.leftMargin = 16;
_player.defaultEdgeControlLayer.rightMargin = 16;
20.8 取消控制層上下視圖的陰影
[_player.defaultEdgeControlLayer.topContainerView cleanColors];
[_player.defaultEdgeControlLayer.bottomContainerView cleanColors];
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容