WWDC2018 Session234: UIKit Apps for Every Size and Shape(2)

本文是针对WWDC2018 Session234:UIKit:Apps for Every Size and Shape快速适配所有型号的iOS移动设备介绍的理解。

Safe area and layout margins(安全区域和布局边距)

如何在应用程序中使用这个属性适应各种屏幕的尺寸和形状呢?

Safe Area: 安全区域,这个概念在iOS11中被提出,安全区域帮助我们将view放置在整个屏幕的可视区域内,保证view不被系统的状态栏、导航栏或tabbar等覆盖,这个概念的提出主要是为了适配像iphoneX一样的全面屏。

我们可以通过UIView的safeAreaInsets来获取安全区域的UIEdgeInsets。如果你使用Auto Layout进行自动布局,你可以使用safeAreaLayoutGuide来确定安全区域,同时安全区域限制了视图的可见部分,如图1所示。

image

安全区域是如何从父视图传递到子视图的呢?

父视图A的安全区域如下图2所示。

image

接下来,我们在视图A上添加一个子视图B,视图B的约束不依赖于视图A的safeAreaLayoutGuide,视图B的左右和底部都超出了父视图的安全区域,如图3所示。

image

然后在视图B上添加子视图C,并设置视图C的约束依赖于视图B的 safeAreaLayoutGuide,结果视图C的可视范围会被限制在图4的黄色区域内,由此看出父视图的安全区域会向上传递。

image

如何扩展安全区域?

通过使用UIViewController的.additionalSafeAreaInsets属性,可以自定义扩展安全区域的大小,并且可以通过viewSafeAreaInsetsDidChange()方法,获取此时视图的安全区域。

例如,如图5,竖屏时,视图本身的安全区域UIEdgeInsets为 (top = 88, left = 0, bottom = 83, right = 0),再设置视图的additionalSafeAreaInsets为(50,50,50,50),结果视图的安全区域就变为了(top = 138, left = 50, bottom = 133, right = 50)黄色区块表示。

image

UIView同样提供了 safeAreaInsetsDidChange()方法用于获取安全区域的UIEdgeInsets。

布局边距layoutMargins

iOS8 中提出了 layoutMargins的概念,使用layoutMargins可以获取和设置子控件显示内容距离父控件的边距。在 iOS11 中,新增了directionalLayoutMargins属性来指定边距。这两个属性的结构定义如下:

typedef struct UIEdgeInsets {
    CGFloat top, left, bottom, right;  
} UIEdgeInsets;
typedef struct NSDirectionalEdgeInsets {
    CGFloat top, leading, bottom, trailing;  
} NSDirectionalEdgeInsets API_AVAILABLE(ios(11.0),tvos(11.0),watchos(4.0));

从定义上看,区别在于将UIEdgeInsets的left和right调整为NSDirectionalEdgeInsets的leading和trailing。这一调整主要是为了Right To Left(RTL)语言下可以进行自动适配,例如:要实现文本每行尾部边距设置为30px,在以前做法则需要判断语言来区分哪些是RTL语言,然后再做设置,iOS11后,可以一步到位。默认情况下,layoutMargin到各边的距离是8个点。通过在 Interface Builder里面勾选 Constrain to margins,它会根据版本在 iOS11 及以上的系统中自动使用 directionalLayoutMargins。通过viewLayoutMarginsDidChange()方法获取当前视图的layoutMargin。

安全区域和布局边距协同作用

正常情况下子视图的布局边距会依赖父视图的安全区域,但是当设置了insetsLayoutMarginsFromSafeArea = false之后,子视图可以达到突破父视图安全区域的布局效果,如 图7、8 所示。

need-to-insert-img

图7

need-to-insert-img

图8

布局之子视图传播

当一个视图的preservesSuperviewLayoutMargins属性为 true 时,在对它的子视图进行布局时,父视图的 margin 也会被考虑在内。如果存在一个子视图的 frame 刚好和父视图的 margin 表示区域有重合,此时设置 preservesSuperviewLayoutMargins为 true ,则子视图会被刚好限制在父视图的 margin 内,如 图9、10 所示。

need-to-insert-img

图9

need-to-insert-img

图10

最小边距

UIViewController存在属性 systemMinimumLayoutMargins,可以对其进行重写,默认情况下 view 的布局边距会受这个属性的返回值制约。如下重写了该属性,则 view 的边距最小会为 [20,20,20,20]。

overridevarsystemMinimumLayoutMargins:NSDirectionalEdgeInsets{returnNSDirectionalEdgeInsets(top:20, leading:20, bottom:20, trailing:20)}

若设置 viewRespectsSystemMinimumLayoutMargins为 false,则 view 布局边距不受 systemMinimumLayoutMargins属性的影响,默认为 [8,8,8,8]。

Scroll views

adjustedContentInset

iOS11 中提出了 UIScrollView的新属性 adjustedContentInset,它的值等于 UIScrollView原有的 contentInset加上 安全区域等 system inset,如 图11 所示。

adjustedContentInset = contentInset + system inset

need-to-insert-img

图11

废弃 Automatic Content Inset

本 Session 再次提到 iOS11 之后废除了原有的 UIViewController属性 automaticallyAdjustsScrollViewInsets取而代之的是 UIScrollView的新增枚举 ContentInsetAdjustmentBehavior,该枚举结构如下:

publicenumContentInsetAdjustmentBehavior:Int{caseautomaticcasescrollableAxescasenevercasealways }

如下图如果设置枚举值为 .always,则默认情况下 scrollView的 adjustedContentInset就等于 safeAreaInsets,即可视区域不会被 navigationBar和 tabBar遮挡,如 图12 所示。

need-to-insert-img

图12

如果将枚举值设置为 .scrollableAxes,则在可以滚动的方向上,或者设置了 alwaysBounceHorizontal/Vertical为 true 的时候,Inset 才会生效。如 图14,页面内容比较少的时候,垂直方向上 scrollView不可滚动,导致文本标题部分被 navigationBar遮挡,如 图13、14 所示。

need-to-insert-img

图13

need-to-insert-img

图14

系统默认设置的枚举值是 .automatic, 这个枚举值基本和 .scrollableAxes表现一致,但唯一不同的是它还秉承了原来 automaticallyAdjustsScrollViewInsets = true的特性,在有 navigationBar且 isTranslucent为 true 时,即使垂直方向上不能够滚动,依然能够调整 Inset 使内容可见,如 图15 所示。

image

图15

如果将枚举值设置为 .nerver,则 scrollView的 Inset 不会受 safeAreaInserts的影响而改变、如 图16 所示。

image

图16

编写自适应的应用程序

隐藏 status bar

若果在一些场景下需要隐藏 status bar,我们一般会这么做:

classArticleViewController:UIViewController{overridevarprefersStatusBarHidden:Bool{returntrue} }

不幸的是在 iPhone X 上面这么写是无效的,在 iPhone X 上面只有在隐藏了 navigationBar的前提下,上面这段代码才会生效,所以苹果官方给出的建议是同时隐藏 navigationBar和 status bar,如 图17 所示。

need-to-insert-img

图17

readableContentGuide & cellLayoutMarginsFollowreadableWidth

iOS9 就提出了 readableContentGuide这一概念,主要是用于一些阅读类应用,在可视宽度较大的时候,希望能够通过布局将阅读区域限定在一定范围,已缓解用户阅读的过程中追踪内容移动头部所造成的疲劳。readableContentGuide的间距大小会随着字体大小、设备不同等因素而发生改变。现这一属性同样兼容 safeAreaInsets。同样的 UITableView的 cellLayoutMarginsFollowreadableWidth属性也同样兼容 safeAreaInsets,如 图18、19、20 所示。

need-to-insert-img

图18

need-to-insert-img

图19

need-to-insert-img

图20

insetsContentViewToSafeArea

UITableView在iOS11开始添加了一个新的属性insetsContentViewsToSafeArea,该属性能够控制 TableViewCell的 ContentView是否被 safeAreaInsets所影响,如 图20、21 所示。

need-to-insert-img

图21

need-to-insert-img

图22

底部按钮布局最佳实践

iPhone X 之后,我们在开发过程中经常会遇到如何布局底部按钮的问题。在本 Session 中官方给出了一种方案,例如设置按钮距底部相对于 superView 的约束为16,约束的 Priority为 999,同时设置按钮底部相对于 safeAreaLayoutGuide的约束值为大于等于 0。即可实现按钮在 iphone X 和 其他设备上的不同布局,如 图23 所示。

image

图23

总结

其实本 Session 并没有提出任何新的属性和方法,最新的属性在 iOS11 SDK 中就已经提出来了。可能很多开发者,在适配iPhone X 的时候遇到的问题也都解决的差不多了。但个人认为这个 Session 还是很有必要的,它将现有的用于适配开发的 UIKit SDK 进行了归纳总结,这将有助于开发者进一步了解这些属性之间的关联关系对快速适配多种尺寸设备的项目开发会有很大帮助。

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

推荐阅读更多精彩内容