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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • 前段时间抽空总结了一下iOS视频播放的基本用法,发现这其中还有一个我们无法绕过的问题,那就是播放界面的旋转与适配。...
    梧雨北辰阅读 31,044评论 14 147
  • 一、屏幕旋转方向监听 1、UIDeviceOrientation:设备方向 iOS 定义了七种设备方向: 当设备方...
    李子哈哈阅读 4,598评论 0 0
  • 在做视频开发时遇到屏幕旋转问题,其中涉及到 StatusBar、 UINavigationController、U...
    千江月_Chen阅读 4,496评论 4 9
  • 需求:单页面旋转或手动旋转,代码注释写的很全想要的旋转效果都可以自行更改实现。 1.了解旋转API 一个布尔值,指...
    Ak98阅读 2,472评论 3 5
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 8,531评论 28 53