iOS多设备适配简史以及相应的API支撑实现

远古的iPhone3和iPhone4时代,设备尺寸都是固定3.5inch,没有所谓的适配的问题,只需要用视图的frame属性进行硬编码即可。随着时间的推移,苹果的设备种类越来越多,尺寸也越来越大,单纯的frame已经不能简单解决问题了,于是推出了AutoLayout技术和SizeClasses技术来解决多种设备的适配问题。一直在做iOS开发的程序员相信在下面的两个版本交界处需要处理适配的坎一定让你焦头烂额过:

  1. iOS7出来后视图控制器的根视图默认的尺寸是占据整个屏幕的,如果有半透明导航条的话也默认是延伸到导航栏和状态栏的下面。这段时间相信你对要同时满足iOS7和以下的版本进行大面积的改版和特殊适配处理,尤其是状态栏的高度问题尤为棘手。

  2. iOS11出来后尤其是iPhoneX设备推出,iPhoneX设备的特殊性表现为顶部的状态栏高度由20变为了44,底部还出现了一个34的安全区,当横屏时还需要考虑左右两边的44的缩进处理。你需要对所有的布局代码进行重新适配和梳理以便兼容iPhoneX和其他设备,这里面还是状态栏的高度以及底部安全区的的高度尤为棘手。

个人认为这两个版本的发布是iOS开发人员遇到的需要大量布局改版的版本。为了达到完美适配我们可能需要写大量的if,else以及写很多宏以及版本兼容来进行特殊处理。当然苹果也为上面两次大改版提供了诸多的解决方案:

  1. iOS7中对视图控制器提供了如下属性来解决版本兼容性的问题:
@property(nonatomic,assign) UIRectEdge edgesForExtendedLayout NS_AVAILABLE_IOS(7_0); // Defaults to UIRectEdgeAll
@property(nonatomic,assign) BOOL extendedLayoutIncludesOpaqueBars NS_AVAILABLE_IOS(7_0); // Defaults to NO, but bars are translucent by default on 7_0.  
@property(nonatomic,assign) BOOL automaticallyAdjustsScrollViewInsets API_DEPRECATED_WITH_REPLACEMENT("Use UIScrollView's contentInsetAdjustmentBehavior instead", ios(7.0,11.0),tvos(7.0,11.0)); // Defaults to YES

@property(nonatomic,readonly,strong) id<UILayoutSupport> topLayoutGuide API_DEPRECATED_WITH_REPLACEMENT("-[UIView safeAreaLayoutGuide]", ios(7.0,11.0), tvos(7.0,11.0));
@property(nonatomic,readonly,strong) id<UILayoutSupport> bottomLayoutGuide API_DEPRECATED_WITH_REPLACEMENT("-[UIView safeAreaLayoutGuide]", ios(7.0,11.0), tvos(7.0,11.0));

  1. iOS11中提出了一个安全区的概念,要求我们的可操作视图都放置在安全区内,并对视图和滚动视图提供了如下扩展属性:
@property (nonatomic,readonly) UIEdgeInsets safeAreaInsets API_AVAILABLE(ios(11.0),tvos(11.0));
- (void)safeAreaInsetsDidChange API_AVAILABLE(ios(11.0),tvos(11.0));

/* The top of the safeAreaLayoutGuide indicates the unobscured top edge of the view (e.g, not behind
 the status bar or navigation bar, if present). Similarly for the other edges.
 */
@property(nonatomic,readonly,strong) UILayoutGuide *safeAreaLayoutGuide API_AVAILABLE(ios(11.0),tvos(11.0));
/* When contentInsetAdjustmentBehavior allows, UIScrollView may incorporate
 its safeAreaInsets into the adjustedContentInset.
 */
@property(nonatomic, readonly) UIEdgeInsets adjustedContentInset API_AVAILABLE(ios(11.0),tvos(11.0));

/* Also see -scrollViewDidChangeAdjustedContentInset: in the UIScrollViewDelegate protocol.
 */
- (void)adjustedContentInsetDidChange API_AVAILABLE(ios(11.0),tvos(11.0)) NS_REQUIRES_SUPER;

/* Configure the behavior of adjustedContentInset.
 Default is UIScrollViewContentInsetAdjustmentAutomatic.
 */
@property(nonatomic) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior API_AVAILABLE(ios(11.0),tvos(11.0));

/* contentLayoutGuide anchors (e.g., contentLayoutGuide.centerXAnchor, etc.) refer to
 the untranslated content area of the scroll view.
 */
@property(nonatomic,readonly,strong) UILayoutGuide *contentLayoutGuide API_AVAILABLE(ios(11.0),tvos(11.0));

/* frameLayoutGuide anchors (e.g., frameLayoutGuide.centerXAnchor) refer to
 the untransformed frame of the scroll view.
 */
@property(nonatomic,readonly,strong) UILayoutGuide *frameLayoutGuide API_AVAILABLE(ios(11.0),tvos(11.0));

这些属性的具体意义这里就不多说了,网络上以及苹果的官方都有很多资料在介绍这些属性的意思。从上面的这些属性中可以看出苹果提出的这些解决方案其主要是围绕解决视图和导航条、滚动视图、状态栏、屏幕边缘之间的关系而进行的。因为iOS7和iOS11两个版本中控制器中的视图和上面所列出的一些内容之间的关系变化最大。

NSLayoutConstraint约束以及iOS9上的封装改进

在iOS6时代苹果推出了AutoLayout的技术解决方案,这是一套采用以相对约束来替代硬编码的解决方法,然而糟糕的方法名和使用方式导致使用成本和代码量的急剧增加。比如下面的一段代码:

    UIButton *button = [self createDemoButton:NSLocalizedString(@"Pop layoutview at center", "") action:@selector(handleDemo1:)];
    button.translatesAutoresizingMaskIntoConstraints = NO;  //button使用AutoLayout
    [scrollView addSubview:button];

    //下面的代码是iOS6以来自带的约束布局写法,可以看出代码量较大。
    [scrollView addConstraint:[NSLayoutConstraint  constraintWithItem:button attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
    
    [scrollView addConstraint:[NSLayoutConstraint  constraintWithItem:button attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeTop multiplier:1 constant:10]];
    
    [scrollView addConstraint:[NSLayoutConstraint  constraintWithItem:button attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:40]];
    
    [scrollView addConstraint:[NSLayoutConstraint  constraintWithItem:button attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeWidth multiplier:1 constant:-20]];

一个简单的将按钮放到一个UIScrollView中去的代码,当用AutoLayout来实现时出现了代码量风暴问题。对于约束的设置到了iOS9以后有了很大的改进,苹果对约束的设置进行了封装,提供了三个类:NSLayoutXAxisAnchor, NSLayoutYAxisAnchor, NSLayoutDimension来简化约束的设置,还是同样的功能用新的类来写约束就简洁清晰很多了:

    UIButton *button = [self createDemoButton:NSLocalizedString(@"Pop layoutview at center", "") action:@selector(handleDemo1:)];
    button.translatesAutoresizingMaskIntoConstraints = NO;  //button使用AutoLayout
    [scrollView addSubview:button];
    [button.centerXAnchor constraintEqualToAnchor:scrollView.centerXAnchor].active = YES;
    [button.topAnchor constraintEqualToAnchor:scrollView.topAnchor constant:10].active = YES;
    [button.heightAnchor constraintEqualToConstant:40].active = YES;
    [button.widthAnchor constraintEqualToAnchor:scrollView.widthAnchor multiplier:1 constant:-20].active = YES;
UIStackView

在iOS9中还提供了一个UIStackView的类来简化那些视图需要从上往下或者从左往右依次添加排列的场景,通过UIStackView容器视图的使用就不再需要为每个子视图添加冗余的依赖约束关系了。在大量的实践中很多应用的各板块其实都是按顺序从上到下排列或者从左到右排列的。所以如果您的应用最低支持到iOS9的话就可以大量的应用这个类来构建你的程序了。

占位视图类UILayoutGuide

在iOS9以前两个视图之间的间距和间隔是无法支持浮动和可伸缩设置的,以及我们可以需要在两个视图之间保留一个浮动尺寸的空白区域,解决的方法是在它们中间加入一个透明颜色的UIView来进行处理,不管如何只要是View都需要进行渲染和绘制从而有可能一定程度上影响程序的性能,而在iOS9以后提供了一个占位视图类UILayoutGuide,这个类就像是一个普通的视图一样可以为它设置约束,也可以将它添加进入视图中去,也可以将这个占位视图作为其他视图的约束依赖项,唯一的不同就是占位视图不会进行任何的渲染和绘制,它只会参与布局处理。因此这个类的引入可以很大程度上解决那些浮动间距的问题。

SizeClasses多屏幕适配

当我们的程序可能需要同时在横屏和竖屏下运行并且横屏和竖屏下的布局还不一致时,而且希望我们的应用在小屏幕上和大屏幕上(比如iPhone8 Plus 以及iPhoneX S Max)的布局有差异时,我们可能需要用到苹果的SizeClasses技术。这是苹果在iOS8中推出来的一个概念。 但是在实际的实践中我们很少有看到使用SizeClasses的例子和场景以及在我们开发中很少有使用到这方面的技术,所以我认为这应该是苹果的一个多屏幕适配的失败解决的方案。从字面理解SizeClasses就是尺寸的种类,苹果将设备的宽和高分为了压缩和常规两种尺寸类型,因此我们可以得到如下几种类型的设备:

设备 方向 类型
iPhone4/5/6/7/X 竖屏 w:Compact h:Regular
iPhone4/5/6/7/X 横屏 w:Compact h:Compact
iPhone6/7Plus, iPhoneXMax 竖屏 w:Compact h:Regular
iPhone6/7Plus, iPhoneXMax 横屏 w:Regular h:Compact
所有iPad 竖屏 w:Regular h: Regular
所有iPad 横屏 w:Regular h: Regular
所有iWatch 竖屏 w: Compact h: Compact
所有iWatch 横屏 w: Compact h: Compact

很欣慰的是如果您的应用是一个带有系统导航条的应用时很多适配的问题都能够得到很好的解决,因为系统已经为你做了很多事情,你不需要做任何特殊的处理。而如果你的应用的某个界面是present出来的,或者是你自己实现的自定义导航条的话,那么你可能就需要自己来处理各种版本的适配问题了。并且如果你的应用可能还有横竖屏的话那这个问题就更加复杂了。

最后除了可以用系统提供的API来解决所有的适配问题外,还向大家推荐我的开源布局库:MyLayout。它同时支持Objective-C以及Swift版本。而且用这个库后上面的所有适配问题都不是问题。


欢迎大家访问欧阳大哥2013的github地址简书地址

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

推荐阅读更多精彩内容