iOS屏幕旋转(横屏/竖屏)

一、屏幕旋转优先级

Target属性配置或者Info.plist设置 = AppDelegate中设置 > 根视图控制器 > 普通视图控制器。
如果高优先级的已经关闭了,低优先级就不会生效,如:AppDelegate中关闭了屏幕旋转,即使在根视图控制器中开启了屏幕旋转,也不会生效。

一、全局权限

1.TARGETS->Device Orientation中设置
image.png

iPhone没有UpSide Down的旋转效果,勾选也不生效。但iPad支持。
对于iPhone,如果四个属性都选或者都不选,效果和默认的情况一样。

2.Info.plist中设置
image.png

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),
}

三、控制屏幕旋转

以下是控制屏幕旋转的函数。不管是自动旋转还是强制旋转,都需要设置shouldAutorotateYESsupportedInterfaceOrientations里对应的为当前界面支持的旋转方向,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就可以支持界面旋转了。如果设置supportedInterfaceOrientationsUIInterfaceOrientationMaskLandscapeRight,则当前界面进入后会不会自动横屏显示,需要设备旋转后才会横屏,如想进入后自动横屏,参考特殊设置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

你可能感兴趣的:(iOS屏幕旋转(横屏/竖屏))