一、屏幕旋转优先级
Target属性配置或者Info.plist设置 = AppDelegate中设置 > 根视图控制器 > 普通视图控制器。
如果高优先级的已经关闭了,低优先级就不会生效,如:AppDelegate中关闭了屏幕旋转,即使在根视图控制器中开启了屏幕旋转,也不会生效。
一、全局权限
1.TARGETS->Device Orientation中设置
iPhone没有UpSide Down的旋转效果,勾选也不生效。但iPad支持。
对于iPhone,如果四个属性都选或者都不选,效果和默认的情况一样。
2.Info.plist中设置
Info.plist里的设置与TARGETS->Device Orientation是一致的,修改一方,另一方会同步修改。
3.AppDelegate中设置
- (UIInterfaceOrientationMask)application:(UIApplication *)application
supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return UIInterfaceOrientationMaskAll;
}
如果在AppDelegate中进行了设置,那么App的全局旋转将以AppDelegate中设置为准,即使前两种方法的设置与这里的不同。
二、局部权限
主要涉及三种视图控制器:UITabbarViewController、UINavigationBarController、UIViewController。
全局权限之后,接下来具有最高权限的就是window的根视图控制器rootViewController了。如果要具体控制单个界面UIViewController的旋转就必须先看一下根视图控制器的配置情况。
如果项目结构为:window的rootViewController为UITabbarViewController,UITabbarViewController管理着若干UINavigationBarController,UINavigationBarController管理着若干UIViewController。
此时的旋转优先级为:UITabbarViewController > UINavigationBarController > UIViewController。
二、屏幕旋转涉及的三种枚举
一、设备方向:UIDeviceOrientation
UIDeviceOrientation是硬件设备(iPhone、iPad等)本身的当前旋转方向,设备方向有7种(包括一种未知的情况),判断设备的方向是以home键的位置作为参照的:
typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
UIDeviceOrientationUnknown,
UIDeviceOrientationPortrait, // Device oriented vertically, home button on the bottom
UIDeviceOrientationPortraitUpsideDown, // Device oriented vertically, home button on the top
UIDeviceOrientationLandscapeLeft, // Device oriented horizontally, home button on the right
UIDeviceOrientationLandscapeRight, // Device oriented horizontally, home button on the left
UIDeviceOrientationFaceUp, // Device oriented flat, face up
UIDeviceOrientationFaceDown // Device oriented flat, face down
}
设备方向只能取值,不能设置。
获取设备当前旋转方向使用方法:[UIDevice currentDevice].orientation
通过监听:UIDeviceOrientationDidChangeNotification
可以检测设备方向变化。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onDeviceOrientationDidChange)
name:UIDeviceOrientationDidChangeNotification
object:nil];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
如果在iPhone的控制台上关闭了屏幕自动旋转,则当手机方向改变时,该通知无法监听到。但可以监听到代码层面主动发起的屏幕旋转。
- (void)deviceOrientationDidChange {
UIDevice *device = [UIDevice currentDevice];
switch (device.orientation) {
case UIDeviceOrientationFaceUp:
NSLog(@"屏幕幕朝上平躺");
break;
case UIDeviceOrientationFaceDown:
NSLog(@"屏幕朝下平躺");
break;
case UIDeviceOrientationUnknown:
//系统当前无法识别设备朝向,可能是倾斜
NSLog(@"未知方向");
break;
case UIDeviceOrientationLandscapeLeft:
NSLog(@"屏幕向左橫置");
break;
case UIDeviceOrientationLandscapeRight:
NSLog(@"屏幕向右橫置");
break;
case UIDeviceOrientationPortrait:
NSLog(@"屏幕直立");
break;
case UIDeviceOrientationPortraitUpsideDown:
NSLog(@"屏幕直立,上下顛倒");
break;
default:
NSLog(@"无法识别");
break;
}
}
二、页面方向:UIInterfaceOrientation
注意:页面方向与设备方向大部分是可以对应的,但左右旋转是反着的。这是因为页面方向如果向左,也就意味着设置需要向右旋转。
typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown,
UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait,
UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
}
三、页面方向:UIInterfaceOrientationMask
这是为了支持多种UIInterfaceOrientation而定义的类型。
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),
}
三、控制屏幕旋转
以下是控制屏幕旋转的函数。不管是自动旋转还是强制旋转,都需要设置shouldAutorotate
为YES
,supportedInterfaceOrientations
里对应的为当前界面支持的旋转方向,preferredInterfaceOrientationForPresentation
这个函数经测试没有被调用,且不起作用,暂不做讨论。
///开启支持设备自动旋转
- (BOOL)shouldAutorotate {
return YES;
}
///支持屏幕旋转的方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAll;
}
/// 默认进入界面显示方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeRight;
}
一、自动旋转
如果配置了上面的旋转函数且没有在iPhone的控制台上关闭屏幕自动旋转,则支持旋转的界面在设备旋转后,会自动进行旋转。如果在控制台关闭了,则自动旋转会失效。
二、强制旋转
如果配置了上面的旋转函数,则可以通过下面两个方法来强制使界面旋转,这两个方法任选其一即可。
//方法1和方法2只有在shouldAutorotate返回YES的时候生效
//方法1:强制屏幕旋转
- (void)setInterfaceOrientation:(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;
[invocation setArgument:&val atIndex:2];
[invocation invoke];
}
}
//方法2:强制屏幕旋转
- (void)setDeviceInterfaceOrientation:(UIDeviceOrientation)orientation {
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:orientation] forKey:@"orientation"];
}
}
四、屏幕旋转场景
假设项目结构为:window的rootViewController为UITabbarViewController,UITabbarViewController管理着若干UINavigationBarController,UINavigationBarController管理着若干UIViewController。
一、全部界面支持旋转
只需设置全局权限。
二、大部分界面竖屏,个别界面旋转
一、通用设置
1.在AppDelegate中设置全局旋转模式为支持旋转
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return UIInterfaceOrientationMaskAll;
}
2.在UITabbarViewController中设置局部权限为跟随当前的子控制器(被选中的tabbar对应的控制器)
//是否自动旋转
-(BOOL)shouldAutorotate {
return self.selectedViewController.shouldAutorotate;
}
//支持哪些屏幕方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return [self.selectedViewController supportedInterfaceOrientations];
}
//默认方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return [self.selectedViewController preferredInterfaceOrientationForPresentation];
}
3.在UINavigationBarController中设置局部权限为跟随当前的子控制器(因为导航控制器是以入栈的形式,故topViewController对应的就是当前显示的UIViewController)
//是否自动旋转
//返回导航控制器的顶层视图控制器的自动旋转属性,
//topViewController是其最顶层的视图控制器,
-(BOOL)shouldAutorotate {
return self.topViewController.shouldAutorotate;
}
//支持哪些屏幕方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return [self.topViewController supportedInterfaceOrientations];
}
//默认方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return [self.topViewController preferredInterfaceOrientationForPresentation];
}
4.在UIViewController中设置当前控制器需要旋转的方向
///开启支持设备自动旋转
- (BOOL)shouldAutorotate {
return YES;
}
///支持屏幕旋转的方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAll;
}
/// 默认进入界面显示方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeRight;
}
5.经过上面的设置后,UIViewController就可以支持界面旋转了。如果设置supportedInterfaceOrientations
为UIInterfaceOrientationMaskLandscapeRight
,则当前界面进入后会不会自动横屏显示,需要设备旋转后才会横屏,如想进入后自动横屏,参考特殊设置2.
如果设置为UIInterfaceOrientationMaskAll
,当前界面默认竖屏。
6.但是上面的设置有瑕疵,会出现不想旋转的UIViewController也能旋转,如果想关闭某些UIViewController的旋转,需要在他们里面重写上面的方法。为了避免这个问题,可以创建个父类:BaseViewController,让所有的UIViewController继承自BaseViewController。在BaseViewController中关闭自动旋转,在需要旋转的UIViewController中重写上面的方法。这样代码比较健全,且避免其他问题出现。
二、特殊设置
1.如果想让界面push时默认竖屏,点击按钮后横屏,同时不受设备旋转的影响(即无论设备怎么旋转,界面方向都保持我们设置的方向),则需要做特殊设置:把supportedInterfaceOrientations
的返回值设置为动态可调整的。
/// 声明一个静态变量标识当前界面支持的旋转方向
static UIInterfaceOrientationMask interfaceOrientations;
/// 默认当前界面只支持竖屏
+ (void)initialize {
interfaceOrientations = UIInterfaceOrientationMaskPortrait;
}
- (IBAction)handleBtnAction:(id)sender {
///在需要横屏时,调整当面界面支持的旋转方向为横屏
interfaceOrientations = UIInterfaceOrientationMaskLandscapeRight;
[self setInterfaceOrientation:UIInterfaceOrientationLandscapeRight];
}
///支持屏幕旋转的方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
///返回设置的当前界面支持的旋转方向
return interfaceOrientations;
}
2.如果想让界面进入后就横屏,则需要在viewWillAppear
时主动调用强制旋转接口:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self setInterfaceOrientation:UIInterfaceOrientationLandscapeRight];
}
- (void)setInterfaceOrientation:(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;
[invocation setArgument:&val atIndex:2];
[invocation invoke];
}
}
///开启支持设备自动旋转
- (BOOL)shouldAutorotate {
return YES;
}
///支持屏幕旋转的方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
///返回设置的当前界面支持的旋转方向
return UIInterfaceOrientationMaskLandscapeRight;
}
/// 默认进入界面显示方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeRight;
}
三、模态视图
模态视图不受上述所有的限制,这是因为模态弹出的视图控制器是隔离出来的,不受根视图控制的影响。
1.在AppDelegate中设置全局旋转模式为支持旋转
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return UIInterfaceOrientationMaskAll;
}
2.设置模态视图的局部权限
///开启支持设备自动旋转
- (BOOL)shouldAutorotate {
return YES;
}
///支持屏幕旋转的方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscapeRight;
}
/// 默认进入界面显示方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeRight;
}
3.模态出该视图
FHSecondVC *vc = [[FHSecondVC alloc] init];
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:^{
}];
注意:模态时一定要设置模式为:UIModalPresentationFullScreen
,旋转才能生效。
其他屏幕旋转文章:
https://www.jianshu.com/p/a354ca1890de