iOS 圖解一個功能很全的視訊播放器的使用

weixin_33866037發表於2018-06-06

大家好, 上一次我分享了一個視訊播放器(SJVideoPlayer), 今天我再給大家分享一下它的使用圖解, 希望大家喜歡. 對了, 本專案除了全屏手勢hook了一下nav的push方法, 其它功能都對原始專案無任何侵害. 您可以放心的使用(全屏手勢如果不順心也可以方便的移除).

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

下面請看這款用心寫的播放器吧. 就從最簡單的開始吧?

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

  • 播放器在普通檢視上播放:
2318691-09585f373eff7211
在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] initWithURL:[NSURL URLWithString:@"http://..."]];
    // 當然也可以指定開始時間. 如下, 從第20秒開始播放
    // _videoPlayer.URLAsset = [[SJVideoPlayerURLAsset alloc] initWithURL:[NSURL URLWithString:@"http://..."] specifyStartTime:20.0];


  • 播放器在UITableViewCell或UICollectionViewCell中播放:
2318691-18c33f4e5fcbb0f6
播放器在UITableViewCell或UICollectionViewCell中播放.png

/// 以下為示例:
/// UICollectionView同UITableView初始化一致, 所以此處僅展示UITableView的示例.
- (void)clickedPlayBtnOnTabCell:(SJVideoListTableViewCell *)cell playerSuperview:(UIView *)playerSuperview {
    //  1. 建立一個播放資源
    SJPlayModel *playModel =
    [SJPlayModel UITableViewCellPlayModelWithPlayerSuperviewTag:playerParentView.tag  // 請務必設定tag, 且不能等於0. 由於重用機制, 當檢視滾動時, 播放器需要通過此tag尋找其父檢視
                                                    atIndexPath:[self.tableView indexPathForCell:cell]
                                                      tableView:self.tableView];
    SJVideoPlayerURLAsset *asset =
    [[SJVideoPlayerURLAsset alloc] initWithURL:[NSURL URLWithString:@"http://..."]
                                     playModel:playModel];

    // 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上播放:


    2318691-d1894aeb69b2db58
    播放器在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] initWithURL:[NSURL URLWithString:@"https://..."]
                                         playModel:[SJPlayModel UITableViewHeaderViewPlayModelWithPlayerSuperview: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中:


    2318691-70b8ddc7ba50d42f
    播放器在UICollectionViewCell中播放, 同時UICollectionView在tableHeaderView中.png

/// 以下為示例:
    __weak typeof(self) _self = self;
    _tableHeaderView.clickedPlayBtnExeBlock = ^(TableHeaderView *view, UICollectionView *collectionView, NSIndexPath *indexPath, UIView *playerSuperview) {
        __strong typeof(_self) self = _self;
        if ( !self ) return;        

        //  1. 建立一個播放資源
        SJPlayModel *playModel = [SJPlayModel UICollectionViewNestedInUITableViewHeaderViewPlayModelWithPlayerSuperviewTag:playerSuperview.tag atIndexPath:indexPath collectionView:collectionView tableView:self.tableView];

        SJVideoPlayerURLAsset *asset =
        [[SJVideoPlayerURLAsset alloc] initWithURL:[NSURL URLWithString:@"https://..."]
                                         playModel:playModel];
        
        // 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中:


    2318691-2f82f8729c95b56c
    播放器在UICollectionCell中播放, 同時UICollectionView在UITableViewCell中.png

/// 以下為示例:
- (void)clickedPlayWithTableViewCell:(NestedTableViewCell *)tabCell
                     playerSuperview:(UIView *)playerSuperview
         collectionViewCellIndexPath:(NSIndexPath *)collectionViewCellIndexPath
                      collectionView:(UICollectionView *)collectionView {
    //  1. 建立一個播放資源
    NSIndexPath *tabCellIndexPath = [self.tableView indexPathForCell:tabCell];

    SJPlayModel *playModel = [SJPlayModel UICollectionViewNestedInUITableViewCellPlayModelWithPlayerSuperviewTag:playerSuperview.tag atIndexPath:collectionViewCellIndexPath collectionViewTag:collectionView.tag collectionViewAtIndexPath:tabCellIndexPath tableView:self.tableView];

    SJVideoPlayerURLAsset *asset =
    [[SJVideoPlayerURLAsset alloc] initWithURL:[NSURL URLWithString:@"https://..."]
                                     playModel:playModel];

    // 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)initWithOtherAsset:(SJVideoPlayerURLAsset *)otherAsset 
                         playModel:(__kindof SJPlayModel *)playModel;

/// 以下為示例:
  // 新介面的播放器, 資源初始化:
    _videoPlayer = [SJVideoPlayer player];
    _videoPlayer.view.frame = CGRectMake(0, 20, 375, 375 * 9/16.0); // 可以使用AutoLayout, 這裡為了簡便設定的Frame.
    [self.view addSubview:_videoPlayer.view];
    // 初始化資源
    _videoPlayer.URLAsset = [SJVideoPlayerURLAsset initWithOtherAsset:otherAsset playModel:[SJPlayModel playModel....]; 

是的, otherAsset即為上一個頁面播放的Asset, 只要用它進行初始化即可實現續播功能. 同時可以發現, 初始化時, 除了需要一個otherAsset, 其他方面同開始的示例一模一樣.

請看下圖:


2318691-fa54404017304342
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, 看下圖對比:
    • 2318691-03d63335eb415dde
      白條.png
    • 2318691-dcf80f12db11eb38
      黑條.png

      額, 我們趕緊說回撥吧, 狀態列還是變成白的好一點. 旋轉前的回撥以及旋轉後的回撥如下:


/// 旋轉前的回撥
@property (nonatomic, copy, nullable) void(^viewWillRotateExeBlock)(__kindof SJBaseVideoPlayer *player, BOOL isFullScreen);
/// 旋轉後的回撥
@property (nonatomic, copy, nullable) void(^viewDidRotateExeBlock)(__kindof SJBaseVideoPlayer *player, BOOL isFullScreen);

/// 以下為示例:

// 旋轉前的示例(我常用旋轉前的block, 旋轉後的block基本沒用過?):
// 1. 設定播放器旋轉前的回撥. 
    _videoPlayer.viewWillRotateExeBlock = ^(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;
}


  • 禁止自動旋轉. 這個功能是必須有的, 如果不禁止旋轉, 請看圖:
2318691-41cea0eeeaaf4d8b
需要禁止旋轉.gif

SJVideoPlayer可以通過如下方式禁止自動旋轉:


// 禁止自動旋轉. 
_videoPlayer.disableAutoRotation = YES;

這裡有兩點需要注意: 1. 返回時要記得恢復自動旋轉. 2. 禁止自動旋轉後, 手動點選全屏按鈕, 還是可以旋轉的.


  • 禁止任何旋轉. 也就是鎖屏. 請看圖:
2318691-0e98cdcbae21d4ce
鎖屏.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
 
 當您想在後臺播放視訊時:
 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.playDidToEndExeBlock = ^(__kindof SJBaseVideoPlayer * _Nonnull player) {
        __strong typeof(_self) self = _self;
        if ( !self ) return ;
        [player replay];
    }

如上, 當播放完畢時, 播放器呼叫 replay 方法, 讓其從頭重新開始播放.


網路狀態變更時的提示

有時候我們需要能夠友好的告訴客戶當前的網路狀態發生了改變, 畢竟流量是要錢的. 我們繼續看圖:


2318691-c8dd1fb181ec14c2
網路狀態變更提示.png

這些提示, 我都做了本地化處理, 支援的語言有: 中文/繁體/英文. 開發者也可以自己定義想要的提示. 後面我會介紹SJVideoPlayer全域性的配置類, 它可以配置各個控制元件的圖片, slider, 本地化的一些提示等等.


夜已深, 請看下回

由於內容比較多, 就介紹到這裡吧(熬夜到1點了... 明天還要上班?), 下一篇我會介紹全域性的配置類, 還有如何無縫的替換掉原始控制層, 同時保留自己想要的控制層(切換器).

對了最後嘮叨一下, 懇請尊重開源作者的勞動成果, 引用或參照還請註明來源. 謝謝.

另外這是我的聯絡方式, 希望與您交流:

專案地址: https://github.com/changsanjiang/SJVideoPlayer

我的郵箱: changsanjiang@gmail.com

如果您有什麼建議, 望請聯絡我!

相關文章