前言
最近在写播放器壳子,里面涉及到全屏/半屏转换,这里分享一下当中遇见的问题和处理思路,感兴趣的老哥可以去下载玩玩,觉得好用有帮助还请点个小星星!!!
Demo地址:KJPlayerDemo
效果预览,
思路分享
目前我所知道的关于全屏就是以下几种处理方案:
原生页面旋转,强制旋转设备旋转,播放器所在控制器旋转为横屏状态
播放器承载的View旋转,使用UIView的transform
属性旋转90度,其实这个并非真正的横屏,系统菜单栏和系统控件等还是保持原先的竖屏状态
baseView.transform = CGAffineTransformMakeRotation(M_PI_2);
旋转View + 横屏Window,这种方式就解决第二种没有旋转系统控件的问题
第三种思路方案
1、存储frame
,后面切回小屏时刻使用
static CGRect _originalFrame; + (CGRect)originalFrame{ return _originalFrame; } + (void)setOriginalFrame:(CGRect)originalFrame{ _originalFrame = originalFrame; }
2、旋转状态栏方向
id<KJPlayerRotateAppDelegate> delegate = (id<KJPlayerRotateAppDelegate>)[[UIApplication sharedApplication] delegate]; NSAssert([delegate conformsToProtocol:@protocol(KJPlayerRotateAppDelegate)], @"Please see the usage documentation!!!"); [delegate kj_transmitCurrentRotateOrientation:UIInterfaceOrientationMaskLandscape]; KJRotateViewController *vc = [[KJRotateViewController alloc] init]; vc.interfaceOrientationMask = UIInterfaceOrientationMaskLandscape; UIWindow *videoWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; videoWindow.rootViewController = vc;
这里配置ViewController来支持旋转的方向,调用Window的rootViewcontroller的supportedInterfaceOrientations
和shouldAutorotate
方向来确定当前页面支持的方向
@implementation KJRotateViewController - (BOOL)shouldAutorotate{ return YES; } - (UIInterfaceOrientationMask)supportedInterfaceOrientations{ return self.interfaceOrientationMask; } @end
3、旋转View
baseView.transform = CGAffineTransformMakeRotation(M_PI_2); baseView.bounds = [UIScreen mainScreen].bounds; baseView.center = baseView.superview.center; [baseView setValue:@(YES) forKey:@"isFullScreen"];
这里有个细节处理,就是我们在旋转动画完成之后,需要将屏幕固定为竖屏方向
id<KJPlayerRotateAppDelegate> delegate = (id<KJPlayerRotateAppDelegate>)[[UIApplication sharedApplication] delegate]; [delegate kj_transmitCurrentRotateOrientation:UIInterfaceOrientationMaskPortrait];
到此全屏模式思路就差不多都出来了,具体需要注意的地方就是需要Appdelegate当中实现KJPlayerRotateAppDelegate
参考文档,iOS端一次视频全屏需求的实现,
我已将之封装成工具
直接贴出实现代码
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> NS_ASSUME_NONNULL_BEGIN /* 必须在Appdelegate当中实现该协议 */ @protocol KJPlayerRotateAppDelegate <NSObject> /* 传递当前旋转方向 */ - (void)kj_transmitCurrentRotateOrientation:(UIInterfaceOrientationMask)rotateOrientation; @end @class KJBasePlayerView; @interface KJRotateManager : NSObject /* 切换到全屏 */ + (void)kj_rotateFullScreenBasePlayerView:(UIView*)baseView; /* 切换到小屏 */ + (void)kj_rotateSmallScreenBasePlayerView:(UIView*)baseView; /* 切换到浮窗屏 */ + (void)kj_rotateFloatingWindowBasePlayerView:(UIView*)baseView; @end NS_ASSUME_NONNULL_END
#import "KJRotateManager.h" #define kRotate_KeyWindow \ ({UIWindow *window;\ if (@available(iOS 13.0, *)) {\ window = [UIApplication sharedApplication].windows.firstObject;\ }else{\ window = [UIApplication sharedApplication].keyWindow;\ }\ window;}) //旋转中间控制器 @interface KJRotateViewController : UIViewController @property (nonatomic, assign) UIInterfaceOrientationMask interfaceOrientationMask; @end @implementation KJRotateViewController - (BOOL)shouldAutorotate{ return YES; } - (UIInterfaceOrientationMask)supportedInterfaceOrientations{ return self.interfaceOrientationMask; } @end /* ************************* 黄金分割线 ***************************/ @interface KJRotateManager () @property(nonatomic,assign,class)CGRect originalFrame; @end @implementation KJRotateManager /* 切换到全屏 */ + (void)kj_rotateFullScreenBasePlayerView:(UIView*)baseView{ self.originalFrame = baseView.frame; UIColor *temp = baseView.superview.backgroundColor; baseView.superview.backgroundColor = UIColor.blackColor; baseView.layer.zPosition = 1; id<KJPlayerRotateAppDelegate> delegate = (id<KJPlayerRotateAppDelegate>)[[UIApplication sharedApplication] delegate]; NSAssert([delegate conformsToProtocol:@protocol(KJPlayerRotateAppDelegate)], @"Please see the usage documentation!!!"); [delegate kj_transmitCurrentRotateOrientation:UIInterfaceOrientationMaskLandscape]; KJRotateViewController *vc = [[KJRotateViewController alloc] init]; vc.interfaceOrientationMask = UIInterfaceOrientationMaskLandscape; UIWindow *videoWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; videoWindow.rootViewController = vc; [UIView animateWithDuration:0.3f animations:^{ baseView.transform = CGAffineTransformMakeRotation(M_PI_2); baseView.bounds = [UIScreen mainScreen].bounds; baseView.center = baseView.superview.center; [baseView setValue:@(YES) forKey:@"isFullScreen"]; } completion:^(BOOL finished) { baseView.superview.backgroundColor = temp; id<KJPlayerRotateAppDelegate> delegate = (id<KJPlayerRotateAppDelegate>)[[UIApplication sharedApplication] delegate]; [delegate kj_transmitCurrentRotateOrientation:UIInterfaceOrientationMaskPortrait]; }]; } /* 切换到小屏 */ + (void)kj_rotateSmallScreenBasePlayerView:(UIView*)baseView{ baseView.layer.zPosition = 0; id<KJPlayerRotateAppDelegate> delegate = (id<KJPlayerRotateAppDelegate>)[[UIApplication sharedApplication] delegate]; NSAssert([delegate conformsToProtocol:@protocol(KJPlayerRotateAppDelegate)], @"Please see the usage documentation!!!"); [delegate kj_transmitCurrentRotateOrientation:UIInterfaceOrientationMaskPortrait]; KJRotateViewController *vc = [[KJRotateViewController alloc] init]; vc.interfaceOrientationMask = UIInterfaceOrientationMaskPortrait; UIWindow *videoWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; videoWindow.rootViewController = vc; baseView.transform = CGAffineTransformIdentity; baseView.frame = self.originalFrame; [baseView setValue:@(NO) forKey:@"isFullScreen"]; } /* 切换到浮窗屏 */ + (void)kj_rotateFloatingWindowBasePlayerView:(UIView*)baseView{ // TODO: } #pragma mark - getter/setter static CGRect _originalFrame; + (CGRect)originalFrame{ return _originalFrame; } + (void)setOriginalFrame:(CGRect)originalFrame{ _originalFrame = originalFrame; } + (UIViewController*)topViewController{ UIViewController *result = nil; UIWindow * window = kRotate_KeyWindow; if (window.windowLevel != UIWindowLevelNormal){ NSArray *windows = [[UIApplication sharedApplication] windows]; for(UIWindow * tmpWin in windows){ if (tmpWin.windowLevel == UIWindowLevelNormal){ window = tmpWin; break; } } } UIViewController *vc = window.rootViewController; while (vc.presentedViewController) { vc = vc.presentedViewController; } if ([vc isKindOfClass:[UITabBarController class]]){ UITabBarController * tabbar = (UITabBarController *)vc; UINavigationController * nav = (UINavigationController *)tabbar.viewControllers[tabbar.selectedIndex]; result = nav.childViewControllers.lastObject; }else if ([vc isKindOfClass:[UINavigationController class]]){ UIViewController * nav = (UIViewController *)vc; result = nav.childViewControllers.lastObject; }else{ result = vc; } return result; } @end
到此,关于全屏模式思路介绍的差不多了,至于详细信息,我Dmeo里面写的也很详细,感兴趣的朋友可以去下载 Demo地址:KJPlayerDemo
使用注意事项
关于全屏/半屏配置信息
请在Appdelegate当中实现该协议KJPlayerRotateAppDelegate
@interface AppDelegate ()<KJPlayerRotateAppDelegate> @property(nonatomic,assign) UIInterfaceOrientationMask rotateOrientation; @end @implementation AppDelegate /* 传递当前旋转方向 */ - (void)kj_transmitCurrentRotateOrientation:(UIInterfaceOrientationMask)rotateOrientation{ self.rotateOrientation = rotateOrientation; } - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{ if (self.rotateOrientation) { return self.rotateOrientation; }else{ return UIInterfaceOrientationMaskPortrait; } } @end
八阿哥总结
果然代码都是经不起测试调试的,一如既往的又出现bug
1、视图层次显示问题
控件KJBasePlayerView
是优先于后面的按钮Add在self.view
上,所以当我按下全屏按钮之时就出现下面的问题 😓😓- -!
解决方案:修改KJBasePlayerView
的zPosition
属性,正常设置的控件对应的layer.zPosition
默认都是0
,所以只需要将控件的layer.zPosition
设置为1
就解决
- (instancetype)initWithFrame:(CGRect)frame{ if (self = [super initWithFrame:frame]) { self.layer.zPosition = 1; [self kj_initializeConfiguration]; } return self; }
2、顶部导航栏问题
现在还有一个问题就是上面的导航栏,这个我目前能想到的笨办法就是,我不是有把当前全屏/半屏状态通过回调返出来嘛,然后在回调当中去控制隐藏/显示
PLAYER_WEAKSELF; self.basePlayerView.kVideoChangeScreenState = ^(KJPlayerVideoScreenState state) { if (state == KJPlayerVideoScreenStateFullScreen) { [weakself.navigationController setNavigationBarHidden:YES animated:YES]; }else{ [weakself.navigationController setNavigationBarHidden:NO animated:YES]; } };
不知哪位大神或者有更好更方便的解决方式,欢迎指点一下,谢谢~😁