在做视频开发时遇到屏幕旋转问题,其中涉及到 StatusBar、 UINavigationController、UITabBarController 、UIViewcontroller
。
在设备锁屏下的整体效果图
iOS-旋转.gif
主要涉及以下4点:
- 横竖屏的旋转
- 屏幕旋转相应改变视图位置
- 旋转时状态栏的隐藏与显示
- 锁屏
1、横竖屏旋转
第1步:
-(UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {// NSLog(@"0000000---------%@",NSStringFromClass([[self topViewController] class]));// if ([NSStringFromClass([[self topViewController] class]) isEqualToString:@"FirstViewController"]) {// //横屏// return UIInterfaceOrientationMaskLandscapeRight;// }// //竖屏// return UIInterfaceOrientationMaskPortrait; NSUInteger orientations = UIInterfaceOrientationMaskAllButUpsideDown; if(self.window.rootViewController){ //取出当前显示的控制器 UIViewController *presentedViewController = [self topViewControllerWithRootViewController:self.window.rootViewController]; //按当前控制器支持的方向确定旋转方向(将旋转方向重新交给每个控制器自己控制) NSLog(@"%s, line = %d",__FUNCTION__,__LINE__); orientations = [presentedViewController supportedInterfaceOrientations]; } return orientations;}//获取界面最上层的控制器//- (UIViewController*)topViewController {// NSLog(@"%s, line = %d",__FUNCTION__,__LINE__);// return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];//}//一层一层的进行查找判断- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController { NSLog(@"%s, line = %d",__FUNCTION__,__LINE__); if ([rootViewController isKindOfClass:[UITabBarController class]]) { UITabBarController* tabBarController = (UITabBarController*)rootViewController; NSLog(@"Tabbar:%@",NSStringFromClass([tabBarController.selectedViewController class])); return [self topViewControllerWithRootViewController:tabBarController.selectedViewController]; } else if ([rootViewController isKindOfClass:[UINavigationController class]]) { UINavigationController* nav = (UINavigationController*)rootViewController; NSLog(@"nav:%@",NSStringFromClass([nav.visibleViewController class])); return [self topViewControllerWithRootViewController:nav.visibleViewController]; } else if (rootViewController.presentedViewController) { NSLog(@"present:%@",NSStringFromClass([rootViewController.presentationController class])); UIViewController* presentedViewController = rootViewController.presentedViewController; return [self topViewControllerWithRootViewController:presentedViewController]; } else { NSLog(@"root:%@",rootViewController); return rootViewController; }}
代码中通过 -(UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
方法将控制器交给自己控制,该方法默认值为 Info.plist
中配置的 Supported interface orientations
项的值。
第2步:在各控制器设置支持的方向
//是否允许旋转(默认允许)- (BOOL)shouldAutorotate { return YES;}- (UIInterfaceOrientationMask)supportedInterfaceOrientations{ //允许旋转的方向 return UIInterfaceOrientationMaskAll;}
其中 - supportedInterfaceOrientations
方法在 iPad 中默认取值为 UIInterfaceOrientationMaskAll
,即默认支持所有屏幕方向;而 iPhone 跟 iPod Touch 的默认取值为 UIInterfaceOrientationMaskAllButUpsideDown
,即支持除竖屏向下以外的三个方向。
在设备屏幕旋转时,系统会调用 - shouldAutorotate
方法检查当前界面是否支持旋转,只有 - shouldAutorotate
返回 YES
的时候, - supportedInterfaceOrientations
方法才会被调用,以确定是否需要旋转界面。
这个是 TabbarController
中设置的,它会影响关联的 UIViewController
的支持方向,需要在 UIViewController
中进一步设置
//此方法来控制能否横竖屏 控制锁屏 - (UIInterfaceOrientationMask)supportedInterfaceOrientations { NSLog(@"%s, line = %d",__FUNCTION__,__LINE__); UIInterfaceOrientationMask inter; if (_lockScreen) { switch (_lockOrientation) { case 1: inter = UIInterfaceOrientationMaskPortrait; break; case 2: inter = UIInterfaceOrientationMaskPortraitUpsideDown; break; case 3: inter = UIInterfaceOrientationMaskLandscapeRight; break; case 4: inter = UIInterfaceOrientationMaskLandscapeLeft; break; default:inter = UIInterfaceOrientationMaskAll; break; } } else { inter = UIInterfaceOrientationMaskAll; } //支持全部方向 return inter; }
第3步:强制转换控制器方向
- (void)setInterOrientation:(UIInterfaceOrientation)orientation { if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) { SEL selector = NSSelectorFromString(@"setOrientation:"); NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]]; [invocation setSelector:selector]; [invocation setTarget:[UIDevice currentDevice]]; int val = orientation; // 从2开始是因为0 1 两个参数已经被selector和target占用 [invocation setArgument:&val atIndex:2]; [invocation invoke]; } }
这样就可以完成横竖屏的切换。
2、屏幕旋转相应改变视图位置
这里先扩展 UIDeviceOrientation & UIInterfaceOrientation
的知识
UIDeviceOrientation
设备的物理方向
UIDeviceOrientation
即我们手持的移动设备的 Orientation
,是一个三围空间,有六个方向,通过 [UIDevice currentDevice].orientation
获取当前设备的方向。
typedef NS_ENUM(NSInteger, UIDeviceOrientation) { UIDeviceOrientationUnknown, UIDeviceOrientationPortrait, UIDeviceOrientationPortraitUpsideDown, // Device oriented vertically, home button on the top 竖屏向下,即头在下,Home 键在上 UIDeviceOrientationLandscapeLeft, // Device oriented horizontally, home button on the right 横屏头在左,Home键在右 UIDeviceOrientationLandscapeRight, // Device oriented horizontally, home button on the left 横屏头在右,Home键在左 UIDeviceOrientationFaceUp, // Device oriented flat, face up UIDeviceOrientationFaceDown // Device oriented flat, face down} ;
UIInterfaceOrientation
界面的显示方向
UIInterfaceOrientation
即我们看到的视图的 Orientation
,可以理解为 statusBar
所在的方向,是一个二维空间,有四个方向, 通过 [UIApplication sharedApplication].statusBarOrientation
即状态栏的方向获取当前界面方向。
// Note that UIInterfaceOrientationLandscapeLeft is equal to UIDeviceOrientationLandscapeRight (and vice versa).// This is because rotating the device to the left requires rotating the content to the right.typedef NS_ENUM(NSInteger, UIInterfaceOrientation) { UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown, UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait, UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown, UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight, UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft}
UIInterfaceOrientationMask
支持的方向
// iOS 6 之后用于控制界面的枚举值typedef NS_OPTIONS(NSUInteger, UIInterfaceOrientationMask) { UIInterfaceOrientationMaskPortrait = (1 << UIInterfaceOrientationPortrait), UIInterfaceOrientationMaskLandscapeLeft = (1 << UIInterfaceOrientationLandscapeLeft), UIInterfaceOrientationMaskLandscapeRight = (1 << UIInterfaceOrientationLandscapeRight), UIInterfaceOrientationMaskPortraitUpsideDown = (1 << UIInterfaceOrientationPortraitUpsideDown), UIInterfaceOrientationMaskLandscape = (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight), UIInterfaceOrientationMaskAll = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown), UIInterfaceOrientationMaskAllButUpsideDown = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),}
由上可以发现:
iOS 6 及之后版本使用的 UIInterfaceOrientationMask
类型来控制屏幕屏幕方向,该类型也新增加了几个枚举取值,可用一个枚举取值来代表多个屏幕方向,使用起来更方便。
注意在 UIInterfaceOrientation
中有注释
Note that UIInterfaceOrientationLandscapeLeft is equal to UIDeviceOrientationLandscapeRight (and vice versa).
This is because rotating the device to the left requires rotating the content to the right,大意是界面的左转相当于设备的右转,如果设备向左转时就需要内容(即界面)向右转。即:
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
下面还会举例说明。
其实 UIDeviceOrientation
与 UIInterfaceOrientation
是两个互不相干的属性,通常情况下会一起出现,在这里正好利用此特性在屏幕旋转后进行重新布局。
第1步:监听 UIDeviceOrientationDidChangeNotification
状态
//监听设备旋转 改变 视图 对应位置 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil];//用来控制横竖屏时调整视图位置- (void)deviceOrientationDidChange{ [self isPortrait]; }
第2步:重新布局
if (_interOrientation == UIInterfaceOrientationPortrait || _interOrientation == UIInterfaceOrientationPortraitUpsideDown) { self.top.constant = 145; self.bottom.constant = 210; } else if (_interOrientation == UIInterfaceOrientationLandscapeRight || _interOrientation == UIInterfaceOrientationLandscapeLeft) { self.top.constant = 40; self.bottom.constant = 50; }
例如:竖屏转横屏
界面竖屏 UIInterfaceOrientationPortrait
->横屏 UIInterfaceOrientationLandscapeRight
,设备方向 UIDeviceOrientationPortrait
-> UIDeviceOrientationLandscapeLeft
,在设备发生变化这个过程触发 UIDeviceOrientationDidChangeNotification
监听,然后进行重新布局。
3、旋转时状态栏的隐藏与显示
这里只记述旋转时状态栏的变化,由竖屏想横屏变化时状态栏会消失。
//在需要的`UIViewController`设置是否隐藏- (BOOL)prefersStatusBarHidden { NSLog(@"%s, line = %d",__FUNCTION__,__LINE__); return NO;}
4、锁屏
锁屏时,不管系统锁屏是否关闭、Push 或 Present 返回后,界面依然保持不变。
第1步:设置锁屏
- (IBAction)lockAction:(UIButton *)sender { if (_lockScreen) { _lockScreen = NO; [sender setTitle:@"锁定屏幕" forState:UIControlStateNormal]; } else { _lockScreen = YES; [sender setTitle:@"解开屏幕" forState:UIControlStateNormal]; } _lockOrientation = _interOrientation; }
第2步:绕过强转
- (void)interfaceOrientation:(UIInterfaceOrientation)orientation { [self isPortrait]; //锁屏情况下 不旋转 if (!_lockScreen) { [self setInterOrientation:orientation]; }
第3步:针对 Push 或 Present 返回后
- (void)viewWillAppear:(BOOL)animated { if (_lockScreen) { //记录返回时的界面状态 [self setInterOrientation:_lockOrientation]; } else { [self isPortrait]; } }
5、 针对特定 UIViewController
方向的支持
-(UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window { if ([NSStringFromClass([[self topViewController] class]) isEqualToString:@"FirstViewController"]) { //横屏 return UIInterfaceOrientationMaskLandscapeRight; } //竖屏 return UIInterfaceOrientationMaskPortrait; }
最后的献上 GitHub 代码,还有2个小的 bug ,有兴趣的朋友欢迎来探讨。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持VEVB武林网。
注:相关教程知识阅读请移步到IOS开发频道。