前言
- 最近在写播放器壳子,里面涉及到全屏/半屏转换,这里分享一下当中遇见的问题和处理思路,感兴趣的老哥可以去下载玩玩,觉得好用有帮助还请点个小星星!!!
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];
}
};
不知哪位大神或者有更好更方便的解决方式,欢迎指点一下,谢谢~😁