iOS視頻播放全屏效果實(shí)現(xiàn)(已兼容iOS13)

概述

最近在做有關(guān)音視頻的項(xiàng)目孩擂,項(xiàng)目中涉及到全屏播放切換的問(wèn)題森爽,最近研究了一下。在此做個(gè)記錄提针,實(shí)現(xiàn)全屏效果我目前能夠用兩種方法實(shí)現(xiàn)藕甩,一種是讓App需要進(jìn)行全屏的頁(yè)面隨著設(shè)備進(jìn)行旋轉(zhuǎn)施敢,另外一種是把需要全屏的view放到window上面,為window添加旋轉(zhuǎn)動(dòng)畫狭莱。在這介紹的是使用window實(shí)現(xiàn)全屏功能僵娃。

補(bǔ)充說(shuō)明

由于在iOS13調(diào)用setStatusBarOrientation無(wú)效,所以做了些調(diào)整腋妙,已經(jīng)兼容 iOS13

效果

fullScreen
#import "VideoFullScreenController.h"

static CGFloat AnimationDuration = 0.3;//旋轉(zhuǎn)動(dòng)畫執(zhí)行時(shí)間

@interface VideoFullScreenController ()

@property (nonatomic, nullable, strong) UIView *playerView;//播放器視圖
@property (nonatomic, nullable, strong) UIButton *btnFullScreen;
@property (nonatomic, nullable, strong) UIView *playerSuperView;//記錄播放器父視圖
@property (nonatomic, assign) CGRect playerFrame;//記錄播放器原始frame
@property (nonatomic, assign) BOOL isFullScreen;//記錄是否全屏

@property (nonatomic, assign) UIInterfaceOrientation lastInterfaceOrientation;//記錄最后一次旋轉(zhuǎn)方向
@property (nonatomic, nullable, strong) UIWindow *mainWindow;

@end

@implementation VideoFullScreenController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.playerView addSubview:self.btnFullScreen];
    [self.view addSubview:self.playerView];
    
    if (@available(iOS 13.0, *)) {
        _lastInterfaceOrientation = [UIApplication sharedApplication].windows.firstObject.windowScene.interfaceOrientation;
    } else {
        _lastInterfaceOrientation = [UIApplication sharedApplication].statusBarOrientation;
    }
    //開(kāi)啟和監(jiān)聽(tīng) 設(shè)備旋轉(zhuǎn)的通知
    if (![UIDevice currentDevice].generatesDeviceOrientationNotifications) {
        [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    }
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(handleDeviceOrientationChange:)
                                                name:UIDeviceOrientationDidChangeNotification object:nil];
}

//設(shè)備方向改變的處理
- (void)handleDeviceOrientationChange:(NSNotification *)notification{
    UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
    switch (deviceOrientation) {
        case UIDeviceOrientationFaceUp:
            NSLog(@"屏幕朝上平躺");
            break;
        case UIDeviceOrientationFaceDown:
            NSLog(@"屏幕朝下平躺");
            break;
        case UIDeviceOrientationUnknown:
            NSLog(@"未知方向");
            break;
        case UIDeviceOrientationLandscapeLeft:
            if (self.isFullScreen) {
                [self interfaceOrientation:UIInterfaceOrientationLandscapeRight];
            }
            
            NSLog(@"屏幕向左橫置");
            break;
        case UIDeviceOrientationLandscapeRight:
            if (self.isFullScreen) {
                [self interfaceOrientation:UIInterfaceOrientationLandscapeLeft];
            }
            
            NSLog(@"屏幕向右橫置");
            break;
        case UIDeviceOrientationPortrait:
            NSLog(@"屏幕直立");
            break;
        case UIDeviceOrientationPortraitUpsideDown:
            NSLog(@"屏幕直立默怨,上下顛倒");
            break;
        default:
            NSLog(@"無(wú)法辨識(shí)");
            break;
    }
}
//最后在dealloc中移除通知 和結(jié)束設(shè)備旋轉(zhuǎn)的通知
- (void)dealloc{
    [[NSNotificationCenter defaultCenter]removeObserver:self];
    [[UIDevice currentDevice]endGeneratingDeviceOrientationNotifications];
}

- (BOOL)shouldAutorotate {
    return NO;
}

- (BOOL)prefersStatusBarHidden {
    if (@available(iOS 13.0, *)) {
        return self.isFullScreen;
    }
    return NO;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    
    return UIInterfaceOrientationMaskPortrait|UIInterfaceOrientationMaskLandscapeLeft|UIInterfaceOrientationMaskLandscapeRight;
}



#pragma mark - private method

- (void)fullScreenAction:(UIButton *)sender {
    
    if (self.isFullScreen) {//如果是全屏,點(diǎn)擊按鈕進(jìn)入小屏狀態(tài)
        [self changeToOriginalFrame];
    } else {//不是全屏骤素,點(diǎn)擊按鈕進(jìn)入全屏狀態(tài)
        [self changeToFullScreen];
    }
    
}

- (void)changeToOriginalFrame {
    
    if (!self.isFullScreen) {
        return;
    }

    [UIView animateWithDuration:AnimationDuration animations:^{
        
        
        [self interfaceOrientation:UIInterfaceOrientationPortrait];
        self.playerView.frame = self.playerFrame;
        
    } completion:^(BOOL finished) {
        
        [self.playerView removeFromSuperview];
        
        [self.playerSuperView addSubview:self.playerView];
        self.isFullScreen = NO;
        //調(diào)用以下方法后先壕,系統(tǒng)會(huì)在合適的時(shí)間調(diào)用prefersStatusBarHidden方法,控制狀態(tài)欄的顯示和隱藏谆甜,可根據(jù)自己的產(chǎn)品控制顯示邏輯
        [self setNeedsStatusBarAppearanceUpdate];
    }];
    
}

- (void)changeToFullScreen {
    if (self.isFullScreen) {
        return;
    }
    
    //記錄播放器視圖的父視圖和原始frame值,在實(shí)際項(xiàng)目中垃僚,可能會(huì)嵌套子視圖,所以播放器的superView有可能不是self.view规辱,所以需要記錄父視圖
    self.playerSuperView = self.playerView.superview;
    self.playerFrame = self.playerView.frame;
    
    CGRect rectInWindow = [self.playerView convertRect:self.playerView.bounds toView:self.mainWindow];
    [self.playerView removeFromSuperview];
    
    self.playerView.frame = rectInWindow;
    [self.mainWindow addSubview:self.playerView];
    
    //執(zhí)行旋轉(zhuǎn)動(dòng)畫
    [UIView animateWithDuration:AnimationDuration animations:^{
        
        UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
        if (orientation == UIDeviceOrientationLandscapeRight) {
            [self interfaceOrientation:UIInterfaceOrientationLandscapeLeft];
        } else {
            [self interfaceOrientation:UIInterfaceOrientationLandscapeRight];
        }
        
        self.playerView.bounds = CGRectMake(0, 0, CGRectGetHeight(self.mainWindow.bounds), CGRectGetWidth(self.mainWindow.bounds));
        self.playerView.center = CGPointMake(CGRectGetMidX(self.mainWindow.bounds), CGRectGetMidY(self.mainWindow.bounds));
        
    } completion:^(BOOL finished) {
       
        self.isFullScreen = YES;
        //調(diào)用以下方法后谆棺,系統(tǒng)會(huì)在合適的時(shí)間調(diào)用prefersStatusBarHidden方法,控制狀態(tài)欄的顯示和隱藏罕袋,可根據(jù)自己的產(chǎn)品控制顯示邏輯
        [self setNeedsStatusBarAppearanceUpdate];
    }];
}

- (void)interfaceOrientation:(UIInterfaceOrientation)orientation {
    if (orientation == UIInterfaceOrientationLandscapeRight || orientation == UIInterfaceOrientationLandscapeLeft) {
        // 設(shè)置橫屏
        [self setOrientationLandscapeConstraint:orientation];
    } else if (orientation == UIInterfaceOrientationPortrait) {
        // 設(shè)置豎屏
        [self setOrientationPortraitConstraint];
    }
}

- (void)setOrientationLandscapeConstraint:(UIInterfaceOrientation)orientation {
    
    [self toOrientation:orientation];
}

- (void)setOrientationPortraitConstraint {
    
    [self toOrientation:UIInterfaceOrientationPortrait];
}

- (void)toOrientation:(UIInterfaceOrientation)orientation {
    // 獲取到當(dāng)前狀態(tài)條的方向------iOS13已經(jīng)廢棄改淑,所以不能根據(jù)狀態(tài)欄的方向判斷是否旋轉(zhuǎn),手動(dòng)記錄最后一次的旋轉(zhuǎn)方向
//    UIInterfaceOrientation currentOrientation = [UIApplication sharedApplication].statusBarOrientation;
    // 判斷如果當(dāng)前方向和要旋轉(zhuǎn)的方向一致,那么不做任何操作
    if (self.lastInterfaceOrientation == orientation) { return; }
    
    if (@available(iOS 13.0, *)) {
        //iOS 13已經(jīng)將setStatusBarOrientation廢棄浴讯,調(diào)用此方法無(wú)效
    } else {
        [[UIApplication sharedApplication] setStatusBarOrientation:orientation animated:NO];
    }
    self.lastInterfaceOrientation = orientation;
    
    // 獲取旋轉(zhuǎn)狀態(tài)條需要的時(shí)間:
    
    [UIView animateWithDuration:AnimationDuration animations:^{
        // 更改了狀態(tài)條的方向,但是設(shè)備方向UIInterfaceOrientation還是正方向的,這就要設(shè)置給你播放視頻的視圖的方向設(shè)置旋轉(zhuǎn)
        // 給你的播放視頻的view視圖設(shè)置旋轉(zhuǎn)
        self.playerView.transform = CGAffineTransformIdentity;
        self.playerView.transform = [self getTransformRotationAngleWithOrientation:self.lastInterfaceOrientation];
        // 開(kāi)始旋轉(zhuǎn)
    } completion:^(BOOL finished) {
        
    }];
}

- (CGAffineTransform)getTransformRotationAngleWithOrientation:(UIInterfaceOrientation)orientation {

    // 根據(jù)要進(jìn)行旋轉(zhuǎn)的方向來(lái)計(jì)算旋轉(zhuǎn)的角度
    if (orientation == UIInterfaceOrientationPortrait) {
        return CGAffineTransformIdentity;
    } else if (orientation == UIInterfaceOrientationLandscapeLeft){
        return CGAffineTransformMakeRotation(-M_PI_2);
    } else if(orientation == UIInterfaceOrientationLandscapeRight){
        return CGAffineTransformMakeRotation(M_PI_2);
    }
    return CGAffineTransformIdentity;
}

#pragma mark - setter

- (void)setIsFullScreen:(BOOL)isFullScreen {
    _isFullScreen = isFullScreen;
    [self.btnFullScreen setTitle:isFullScreen?@"退出全屏":@"全屏" forState:UIControlStateNormal];
}

#pragma mark - getter

- (UIView *)playerView {
    if (!_playerView) {
        _playerView = [[UIView alloc]init];
        _playerView.backgroundColor = [UIColor redColor];
        
        if (@available(iOS 11.0, *)) {
            _playerView.frame = CGRectMake(0, self.view.safeAreaInsets.top, CGRectGetWidth(self.view.bounds), CGRectGetWidth(self.view.bounds) * 9 / 16.f);
        } else {
            _playerView.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), CGRectGetWidth(self.view.bounds) * 9 / 16.f);
        }
        
    }
    return _playerView;
}

- (UIButton *)btnFullScreen {
    if (!_btnFullScreen) {
        _btnFullScreen = [UIButton buttonWithType:UIButtonTypeCustom];
        [_btnFullScreen setTitle:@"全屏" forState:UIControlStateNormal];
        _btnFullScreen.backgroundColor = [UIColor orangeColor];
        [_btnFullScreen addTarget:self action:@selector(fullScreenAction:) forControlEvents:UIControlEventTouchUpInside];
        _btnFullScreen.frame = CGRectMake(50, 80, 150, 50);
    }
    return _btnFullScreen;
}

- (UIWindow *)mainWindow {
    if (!_mainWindow) {
        if (@available(iOS 13.0, *)) {
            _mainWindow = [UIApplication sharedApplication].windows.firstObject;
        } else {
            _mainWindow = [UIApplication sharedApplication].keyWindow;
        }
    }
    return _mainWindow;
}

@end

結(jié)尾

如果你的rootViewController是UIViewController的話朵夏,那么用上面的代碼實(shí)現(xiàn)全屏效果沒(méi)有問(wèn)題,如果你的rootViewController是UINavigationController或者UITabBarController的話榆纽,那么還要增加兩個(gè)分類文件仰猖。文件內(nèi)容如下:

一、定義分類UINavigationController+Rotation.h

@implementation UINavigationController (Rotation)

- (BOOL)shouldAutorotate {
    return [self.topViewController shouldAutorotate];
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return [self.topViewController supportedInterfaceOrientations];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return [self.topViewController preferredInterfaceOrientationForPresentation];
}

二奈籽、定義分類UITabBarController+Rotation.h

@implementation UITabBarController (Rotation)

- (BOOL)shouldAutorotate {
    return [self.selectedViewController shouldAutorotate];
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return [self.selectedViewController supportedInterfaceOrientations];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return [self.selectedViewController preferredInterfaceOrientationForPresentation];
}

Demo地址:https://github.com/latacat/iOS_Demos.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末饥侵,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子衣屏,更是在濱河造成了極大的恐慌躏升,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狼忱,死亡現(xiàn)場(chǎng)離奇詭異膨疏,居然都是意外死亡一睁,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門佃却,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)者吁,“玉大人,你說(shuō)我怎么就攤上這事双霍。” “怎么了批销?”我有些...
    開(kāi)封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵洒闸,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我均芽,道長(zhǎng)丘逸,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任掀宋,我火速辦了婚禮深纲,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘劲妙。我一直安慰自己湃鹊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布镣奋。 她就那樣靜靜地躺著币呵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪侨颈。 梳的紋絲不亂的頭發(fā)上余赢,一...
    開(kāi)封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音哈垢,去河邊找鬼妻柒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛耘分,可吹牛的內(nèi)容都是我干的举塔。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼求泰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼啤贩!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起拜秧,我...
    開(kāi)封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤痹屹,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后枉氮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體志衍,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡暖庄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了楼肪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片培廓。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖春叫,靈堂內(nèi)的尸體忽然破棺而出肩钠,到底是詐尸還是另有隱情,我是刑警寧澤暂殖,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布价匠,位于F島的核電站,受9級(jí)特大地震影響呛每,放射性物質(zhì)發(fā)生泄漏踩窖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一晨横、第九天 我趴在偏房一處隱蔽的房頂上張望洋腮。 院中可真熱鬧,春花似錦手形、人聲如沸啥供。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)滤灯。三九已至,卻和暖如春曼玩,著一層夾襖步出監(jiān)牢的瞬間鳞骤,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工黍判, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留豫尽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓顷帖,卻偏偏與公主長(zhǎng)得像美旧,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贬墩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 由于美股10月的暴跌加之中期選舉榴嗅,川普有意在貿(mào)易站上做些文章,正好亞太經(jīng)過(guò)10月的血洗之后陶舞,正在醞釀著一波反彈嗽测,所...
    夜如此沉淪閱讀 420評(píng)論 0 0
  • 這其中的奧秘全都在本文里了唠粥。其實(shí)說(shuō)來(lái)也很簡(jiǎn)單疏魏,很容易理解,只是您從來(lái)未真正從科學(xué)角度了解而已晤愧。您要知道大莫,孩子的學(xué)習(xí)...
    鐸耀閱讀 242評(píng)論 0 0
  • C提供的預(yù)處理功能主要有以下三種: 1.宏定義:#define 標(biāo)識(shí)符 字符串 例如:# define PI ...
    TG帥閱讀 164評(píng)論 0 0
  • 最近發(fā)生的事情只厘,讓我不得不再一次承認(rèn),自己是一個(gè)遇到問(wèn)題會(huì)逃避的人舅巷「嵛叮可能是表現(xiàn)型人格在作祟——只在乎當(dāng)下的表現(xiàn)與喜...
    鄭在踐行閱讀 288評(píng)論 2 1