概述
最近在做有關(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
效果
#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];
}