关于iOS横竖屏适配

关于横竖屏适配,有一句说一句,坑挺深的。之前做Vision和毕设的时候就处理过横竖屏问题,不过当时的功力太浅,明显没有处理明白。所以这次在公司项目中又一次遇到了这种横竖屏的需求,自然要认真的搞一哈,顺便总结一下分享给大家。其实在我理解上,只要明白以下几点,横竖屏处理上并不是问题。大家按需跳转吧:

1.横竖屏方向枚举

2.开启横竖屏权限

3.在VC中如何开启横竖屏

4.横竖屏控制优先级

5.横竖屏适配

6.总结

1.横竖屏方向枚举

关于横竖屏一共有三种枚举,UIInterfaceOrientation,UIInterfaceOrientationMask,UIDeviceOrientation。

1.1 UIInterfaceOrientation与UIDeviceOrientation

为什么这两个放在一起说,好吧,你看看下面这个枚举定义:

typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
    UIInterfaceOrientationUnknown            = UIDeviceOrientationUnknown,
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
}

我相信你应该看出了点东西了把,对于iOS设备来讲,屏幕状态由以上五种状态。上下翻转还是很好区分的,左右旋转可能就不是很好区分。

这里有个坑!!!
高能预警:
请仔细观察上面的枚举值。

在处于竖屏和上下翻转的状态下这两个枚举值是一样的,而当处于横屏时,这两个值刚好相反。

所以在有时你发现跟你预期的翻转方向不一样的时候,可能你用错了枚举。

UIDeviceOrientation 是设备的当前所处的方向,而且事实上它有6个值,

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
}

分别对应iPhone未知方向,竖直,上下反转,向左旋转,向右旋转,屏幕朝上,屏幕朝下。关于横屏如何去分左右,其实API中的注释已经说明,当处于UIDeviceOrientationLandscapeLeft,home键在右侧,当处于UIDeviceOrientationLandscapeRight,home键在左侧。

所以,UIDevice顾名思义,事实上是用来判断设备方向的。

UIInterfaceOrientation 即当前页面的方向。

在设备进行横屏旋转的时候,为了横屏时上下不翻转,所以当Device处于Left时,界面应该是Right旋转。这样才不会使横屏时内容上下翻转。所以我想你应该明白了为什么在处于横屏时为什么他们俩的值是刚好相反的。

所以对于横竖屏适配,使用的枚举大家一定要看好,使用UIInterfaceOrientation。不要搞反。

1.2 UIInterfaceOrientationMask

其实苹果大大还是给了我们更清晰和方便的枚举如下:

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

事实上我们在横竖屏适配时,最常用的是这个枚举。这个枚举详细的列举了各种你需要的情况。我就不赘述了。官方的命名还是很舒服很好理解的。

2.开启横竖屏权限

开启横竖屏的方式有两种,一种是在项目中直接进行勾选,

image 1

可以看到这种勾选方式允许你进行四个方向的配置,并且这种勾选方式会直接在你的项目plist文件中添加

image 2

但是由于在这里配置是对项目启动时lanuch界面产生影响,而往往我们又没有对lanuch进行横竖屏适配,所以在这个时候我们就需要使用第二种方式进行配置。

在项目中的AppDelegate文件中进行配置。

#pragma mark - InterfaceOrientation //应用支持的方向
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
    return UIInterfaceOrientationMaskAllButUpsideDown;
}

搭配UIInterfaceOrientationMask使用,你可以很方便的让你项目开启你所需要的横竖屏权限和限制条件。

3.在VC中如何控制横竖屏

我们都知道MVC架构,那么显而易见,在我们开启了项目的横竖屏的限制之后,需要在ViewController进行相应的配置,才能真正实现横竖屏。

开启横竖屏,我们需要在VC中添加如下代码:

// 设备支持方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskAll;
}
// 默认方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationPortrait; // 或者其他值 balabala~
}

而对于横竖屏,手机端一般有两种情况,一种是手机没有开启横竖屏锁定,用户将手机横屏时触发的。对于第一种情况,我们只需要在VC中添加:

// 开启自动转屏
- (BOOL)shouldAutorotate {
    return YES;
}

另一种是我们在项目中的某些条件下强行让屏幕横屏,例如大图预览,视频播放,等等。而对于这种情况,我们可以使用下面👇这两种方法,都可以实现效果:

- (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];
    }
}

- (void)setInterfaceOrientation:(UIDeviceOrientation)orientation {
    if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
        [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:orientation] forKey:@"orientation"];
    }
}

PS:这两个方法只有在- (BOOL)shouldAutorotate( return YES; )时,才会生效。并且请注意使用的枚举值的不同。

4.横竖屏控制优先级

在我们接手一个项目后,说要添加一个某个界面横竖屏需求时,发现按照上面的方式配置了一圈,发现还是转!不!成!功!What F***!!!

事实上在这里我们要了解一个问题,就是关于横竖屏控制的优先级。对于限于VC范围来讲优先级最高的是当前的windowrootViewController,而往往我们的项目结构是容器视图控制器控制VCtabBarController控制navigationController之后是VC,而横竖屏控制的优先级也是跟你的项目架构一样。而且是一旦优先级高的关闭了横竖屏配置,优先级低的无论如何配置都无法做到横竖屏。所以在你接受这个需求的时候,你需要看一下根视图的配置。

对于这种情况,我们有两种处理方式,一种是通过模态的方式跳转的下个VC,这个VC是隔离出来的,不在你之前的容器里,不会受到rootViewController的影响。

而另一种我们需要改造一下根视图的配置:

-(BOOL)shouldAutorotate {  
    return [[self.viewControllers lastObject] shouldAutorotate];  
}  

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {  
     return [[self.viewControllers lastObject] supportedInterfaceOrientations];  
}  

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {  
    return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];  
}

或者

-(BOOL)shouldAutorotate {
    if ([[self.viewControllers lastObject]isKindOfClass:[NewViewController class]]) {
        return YES;
    }
    return NO;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    if ([[self.viewControllers lastObject]isKindOfClass:[NewViewController class]]) {
        return UIInterfaceOrientationMaskLandscapeLeft;
    }
    return UIInterfaceOrientationMaskPortrait;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    if ([[self.viewControllers lastObject]isKindOfClass:[NewViewController class]]) {
        return UIInterfaceOrientationLandscapeLeft;
    }
    return UIInterfaceOrientationPortrait;
}

可以看到我们通过获取push栈中的最后一个VC的属性,或指定特殊的VC来进行rootViewController的横竖屏设置。

当然也可以通过NSNotificationCenter或者NSUserDefaults的方式对这里的值进行设置,在这里我就不过多赘述了。

总之要知道优先级的问题,general == appDelegate >> rootViewController >> nomalViewController

明白了权限的优先级以及开启的方法我想转屏就很显而易见了。

5.横竖屏适配

事实上旋转屏幕成功,对于iOS横竖屏问题我们只是完成了一半。另一半就是UI适配问题,其实这个要说起来就比较麻烦了,有些时候有很多case需要针对对应的业务条件来定制。但是无外乎几种实现思路。这里博主给大家抛几块砖哈:

首先我们要知道,当发生转屏事件时,系统的回调方法是:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    
    if (size.width > size.height) { // 横屏
        // 横屏布局 balabala
    } else {
        // 竖屏布局 balabala
    }
}

首推的方式是使用约束布局,在使用约束布局时,横竖屏转换时,在通常情况下约束条件会很相似,所以在布局上会极大的减少代码量。其次如果有个别的特殊问题,可以在上面的回调方法里面进行微调。

其次,对于转屏后,[UIScreen mainScreen].bounds.size以及self.view.frame.size的宽高系统会自动调换。即在横屏的时候width > height。所以在我们进行常规布局的时候我们可以选择控件的frame属性都与这两个属性进行比例换算。这样在当横竖屏转换的时候,重布局时,也会适应成对应屏幕下的布局。同样有需要特殊处理的布局,在上面的回调方法中进行细节微调即可。

对于子视图,在横竖屏切换时,会触发子视图重布局的方法:

- (void)layoutSubviews {
    [super layoutSubviews];
    // 通过状态栏电池图标来判断屏幕方向
    if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationMaskPortrait) {
        // 竖屏 balabala
    } else {
        // 横屏 balabala
    }
}

当然我只是说了几种比较简单的处理方式,和应对方法,对于整个项目都需要横竖屏适配的,我想还是一个比较复杂的过程。在实在处理不了的问题上,也可以通过写两套布局的方式来处理。至于过场动画,理论上如果你用约束和我说的比例布局的方式来写,基本系统会自动帮你这个问题给处理掉。但如果两种布局差距很大,你用了两套完全不同的布局,那这个你可能就要伤脑筋了。哈哈哈。不过有一些情况处理要求不严格的话可以使用截图过场大法来解决。不过这个就不是本文的设计范围了。大家如果感兴趣可以自己google一下。当然我日后的文章也可能会写到这了。到时候再来这里修改。

6.总结

iOS横竖屏适配,确实有很多坑,当然,有些坑是系统的;而有些坑,是因为我们的无知而造成的。所以多看多学多做多理解,必然能让你学到更多,增强填坑的硬实力。而对于横竖屏适配这块,转屏并不难,难的是横竖屏布局适配。博主只是简单的说了一些思路,至于实现起来,还是要针对对应的需求来进行处理。下面👇给大家链接几个博主觉得还不错的关于适配的文章,大家可以看看:

iPad横竖屏下的代码适配

iOS 屏幕适配,autoResizing autoLayout和sizeClass图文详解

博主私人博客@HarwordLiu

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

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,551评论 4 58
  • 1.尺寸适配1.原因 iOS7中所有导航栏都为半透明,导航栏(height=44)和状态栏(height=20)不...
    LZM轮回阅读 6,019评论 1 4
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,472评论 25 707
  • 早上很早就去学校,放学故意在学校耽搁很久才走,骆月不再和秦空一起上下学了。 骆月这才发现,如果不是她主动靠近,即使...
    宁我阅读 266评论 0 1
  • 好妹妹乐队有一首歌 我到外地去看你 “最后我们没有在一起 没有在一起 好在我们已反复练习 习惯了别离 抱歉我所有...
    MeredithY阅读 377评论 0 0