iOS视频播放全屏效果实现(已兼容iOS13)

概述

最近在做有关音视频的项目,项目中涉及到全屏播放切换的问题,最近研究了一下。在此做个记录,实现全屏效果我目前能够用两种方法实现,一种是让App需要进行全屏的页面随着设备进行旋转,另外一种是把需要全屏的view放到window上面,为window添加旋转动画。在这介绍的是使用window实现全屏功能。
补充说明
由于在iOS13调用setStatusBarOrientation无效,所以做了些调整,已经兼容 iOS13
效果
fullScreen
#import "VideoFullScreenController.h"static CGFloat AnimationDuration = 0.3; //旋转动画执行时间@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; //记录最后一次旋转方向 @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; } //开启和监听 设备旋转的通知 if (![UIDevice currentDevice].generatesDeviceOrientationNotifications) { [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; } [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(handleDeviceOrientationChange:) name:UIDeviceOrientationDidChangeNotification object:nil]; }//设备方向改变的处理 - (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(@"无法辨识"); break; } } //最后在dealloc中移除通知 和结束设备旋转的通知 - (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) {//如果是全屏,点击按钮进入小屏状态 [self changeToOriginalFrame]; } else {//不是全屏,点击按钮进入全屏状态 [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; //调用以下方法后,系统会在合适的时间调用prefersStatusBarHidden方法,控制状态栏的显示和隐藏,可根据自己的产品控制显示逻辑 [self setNeedsStatusBarAppearanceUpdate]; }]; }- (void)changeToFullScreen { if (self.isFullScreen) { return; }//记录播放器视图的父视图和原始frame值,在实际项目中,可能会嵌套子视图,所以播放器的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]; //执行旋转动画 [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; //调用以下方法后,系统会在合适的时间调用prefersStatusBarHidden方法,控制状态栏的显示和隐藏,可根据自己的产品控制显示逻辑 [self setNeedsStatusBarAppearanceUpdate]; }]; }- (void)interfaceOrientation:(UIInterfaceOrientation)orientation { if (orientation == UIInterfaceOrientationLandscapeRight || orientation == UIInterfaceOrientationLandscapeLeft) { // 设置横屏 [self setOrientationLandscapeConstraint:orientation]; } else if (orientation == UIInterfaceOrientationPortrait) { // 设置竖屏 [self setOrientationPortraitConstraint]; } }- (void)setOrientationLandscapeConstraint:(UIInterfaceOrientation)orientation {[self toOrientation:orientation]; }- (void)setOrientationPortraitConstraint {[self toOrientation:UIInterfaceOrientationPortrait]; }- (void)toOrientation:(UIInterfaceOrientation)orientation { // 获取到当前状态条的方向------iOS13已经废弃,所以不能根据状态栏的方向判断是否旋转,手动记录最后一次的旋转方向 //UIInterfaceOrientation currentOrientation = [UIApplication sharedApplication].statusBarOrientation; // 判断如果当前方向和要旋转的方向一致,那么不做任何操作 if (self.lastInterfaceOrientation == orientation) { return; }if (@available(iOS 13.0, *)) { //iOS 13已经将setStatusBarOrientation废弃,调用此方法无效 } else { [[UIApplication sharedApplication] setStatusBarOrientation:orientation animated:NO]; } self.lastInterfaceOrientation = orientation; // 获取旋转状态条需要的时间:[UIView animateWithDuration:AnimationDuration animations:^{ // 更改了状态条的方向,但是设备方向UIInterfaceOrientation还是正方向的,这就要设置给你播放视频的视图的方向设置旋转 // 给你的播放视频的view视图设置旋转 self.playerView.transform = CGAffineTransformIdentity; self.playerView.transform = [self getTransformRotationAngleWithOrientation:self.lastInterfaceOrientation]; // 开始旋转 } completion:^(BOOL finished) {}]; }- (CGAffineTransform)getTransformRotationAngleWithOrientation:(UIInterfaceOrientation)orientation {// 根据要进行旋转的方向来计算旋转的角度 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

结尾
如果你的rootViewController是UIViewController的话,那么用上面的代码实现全屏效果没有问题,如果你的rootViewController是UINavigationController或者UITabBarController的话,那么还要增加两个分类文件。文件内容如下:
【iOS视频播放全屏效果实现(已兼容iOS13)】一、定义分类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

    推荐阅读