iOS 9 Auto Layout界面自动布局系列6-自适应布局

设计MM小尹:“小李,邮件里是我们设计的用户登录界面初稿,请你看一下技术上有没有什么问题。”
程序猿小李:“好的,我看一下。”
小李打开邮件,看到界面设计初稿为:

这里写图片描述

这是一个同时支持iPhone和iPad的统一App(Universal App)。尽管小李对Auto Layout很熟悉,但是看到设计稿之后,小李却犯了难。因为设计稿中的iPhone横屏的界面布局方式是特殊情况,如果按照图1的方式添加一套自动布局约束,程序就需要判断设备类型(当前是iPhone还是iPad?)以及当前设备的方位(是横屏还是竖屏?)。如果是iPhone横屏,那么还得按照图2的要求重新修改约束。《自动布局系列5》中提到了修改界面约束的3种方式:改约束的常量值,删除旧约束添加新约束,修改约束的优先级。不管哪种方式,对于我们的这个例子来说,都是很麻烦的,很容易出错的。你得在iPhone竖屏转横屏时改一堆约束,然后横屏转竖屏时再改回来。且不说这个界面的层次不算复杂,要是真的遇到一个很复杂的、界面层次很深的界面,对开发者来讲那简直就是一场噩梦。

小李遇到的困惑,我想大多数读者也都曾遇到过。有读者aa3214560也在我的博客上提出了这样的问题:

aa3214560 2015-10-27 20:27发表:
博主,有个自动布局的问题很疑惑。希望能帮助解释一下。在ios中的xib或者storyboard中使用自动布局,是如何适配各种屏幕的。 比如说iphone5的高度是568,6的高度是667,当有一个控件是距上边距600的时候,这在iphone5如何显示(当然,可以都使用距离下边距多少来解决)。或者是当一个textfield距离左边100,右边100的时候,在iphone6上这个大小可能很好,但是在iphone5上就变窄了点。 这里想知道的是,autolayout距离上、下、左、右的距离都是写死的,这在适配上的作用还是不大啊? 难道是在写一套约束,或者在一个约束的基础上进行间距的调整,使在各个屏幕上都能较为完美的显示(一套约束总感觉不完美)? 总感觉自己卡在某个点上。 希望能帮助解疑一下,非常感谢。

其实问题的核心,就在于一套自动布局约束很难灵活地适应视图在不同大小情况下的所有布局。为了解决这个问题,苹果在iOS 8 SDK中提出了Adaptive Layout(姑且翻译为:自适应布局吧),这也是本篇文章要讲解的内容。如果小李的App还需要支持iOS 6和7,那么很遗憾本篇内容帮不了他,他只能使用一套约束并修改。如果仅需支持iOS 8及以上版本,那么他就可以使用自适应布局来轻松解决这个问题。

一、尺寸类型

描述一个2维图形的尺寸,最简单的就是它的水平和垂直方向是大还是小。例如,高清电视的屏幕,水平方向大,垂直方向小。这里的大和小只是概念上相对而言的,拿电影院的荧幕和高清电视屏幕相比,其水平和垂直方向都是很大的。拿笔记本电脑的屏幕和高清电视屏幕比,其水平和垂直方向都是很小的。

在自适应布局中,苹果提出了Size Class(尺寸类型)的概念,用于在概念上表示水平或垂直方向的大小。共有3种类型尺寸,大的称之为Regular(标准尺寸类型,简记为+),小的称之为Compact(紧凑尺寸类型,简记为-),最后一种后面会提到。

下面的表格列出了iOS设备的尺寸类型:

这里写图片描述

在本文中我们约定用以下格式描述:

[水平尺寸类型,垂直尺寸类型]

这个表格展示了不同设备的不同方位下,水平方向与垂直方向的默认尺寸类型:

  • 所有的iPad不管什么方位都是[+, +]
  • 所有的iPhone在竖屏时都是[-, +]
  • 在横屏时只有iPhone 6 Plus和6s Plus是[+, -],其余的iPhone都是[-, -]

那么尺寸类型有什么卵用么?简言之就是我们可以针对不同的尺寸类型,设计与之对应的一套自动布局。例如,我可以设计一套针对[+, +]的自动布局在iPad上使用,其余的都使用另外一套自动布局在iPhone上使用,这样不就能很好地解决之前的问题了么。

这里插一句,除了标准和紧凑外,还有一个尺寸类型是Any(任意尺寸类型,简记为*)。我们都知道类的继承,把共性的、抽象的东西放在基类中,把差异化的、具体的东西放在子类中。苹果也希望我们能够以类似的方式来处理自适应布局,这里的*类似于通配符,既可以匹配+也可以匹配-。所以,当设计人员给出了不同设备上的界面设计稿之后,作为开发人员的我们应该首先总结出最通用的自动布局方案,将其作为任意尺寸类型的自动布局(基类);把差异化的布局放在某个特定尺寸类型的自动布局(子类)。以上图中的例子来说,图1可以作为通用布局方式,而图2可以作为特殊布局方式。

这么说可能有些难以理解,我们不妨以一个实际的例子来说明如何在Interface Builder中使用尺寸类型实现自适应布局。

二、在IB中使用Universal Storyboard实现自适应布局

作者已经按照图1的要求添加好自动布局约束了(作者使用的Xcode版本为7.1.1),项目文件下载地址:

http://yunpan.cn/cLn6aQy5UUBiI (提取码:784c)

打开Main.storyboard,在右侧工具窗口中点击文件审查窗口(快捷键⌥⌘1),在Interface Builder Document栏可以看到Use Auto Layout复选框和Use Size Classes复选框都已选中,表示此Storyboard文件当前使用自动布局并且使用尺寸类型。

这里写图片描述

当使用Xcode 6及后续版本创建新项目时,这两个选项都是默认选中的。我们再把目光转向界面设计器,当使用尺寸类型后,View Controller的主界面默认显示为正方形。请注意,IB中显示的界面尺寸仅仅是示意,并不代表程序运行的真实情况!很多人包括我自己,也曾犯过这个错误,总以为界面设计器就代表运行结果。从IB的角度来说,界面既可以以iPad大屏幕展现,又可以以iPhone小屏幕展现,那IB究竟以什么尺寸来展现?干脆就以正方形展现吧,这样反而不容易引起误解。

说句题外话,如果你确实想以某种尺寸或者某种方位来设计界面,可以选中View Controller,在右侧工具窗口中点击属性审查窗口(快捷键⌥⌘4),在Simulated Metrics栏的Size下拉菜单和Orientation下拉菜单中进行选择。在这里还可以设置状态栏、顶部底部栏等,同样的它们也都不代表运行时的真实情况。

这里写图片描述

现在打开助手视图(快捷键⌥⌘↩)并预览Storyboard,可以看到iPad横竖屏和iPhone竖屏都符合要求,但是iPhone横屏不是图2的样子。

这里写图片描述

切换回标准视图(快捷键⌘↩),注意到IB下方的“w Any h Any”按钮(下文简称尺寸类型按钮),此按钮显示的就是当前的尺寸类型。w代表width,即水平方向。Any是任意尺寸类型。h代表height,即垂直方向。因此“w Any h Any”就是[*, *]。点击该按钮会弹出一个9宫格(下文简称尺寸类型弹窗),正好是所有尺寸类型的组合情况(水平方向和垂直方向各3种的全排列),我们可以在此处切换尺寸类型。当把鼠标移到某个宫格上时,会提示对应的尺寸类型与设备方位情况。例如下图左所示为[*, *],是一个父布局,泛指所有布局情况。下图右所示为[*, -],也是一个父布局,表示高度为紧凑时的布局,对应所有iPhone横屏情况。

这里写图片描述

现在我们需要按照图2的要求特殊处理iPhone横屏界面布局。按照上图右所示,选中Compact Height尺寸类型,此时IB下方工具栏变为蓝色,尺寸类型按钮显示“w Any h Compact”,表示当前我们已经切换到某一特定的尺寸类型。点击头像,打开尺寸审查窗口(快捷键⌥⌘5)查看其约束,可知头像与下方用户名文本框水平中心对齐,且头像底部与用户名文本框顶部垂直间距为20。

这里写图片描述

现在需要让头像垂直居中其父视图,且左侧距离其父视图左侧60。单击“Align Center X to: 用户名”约束,按Delete键。单击“Bottom Space to: 用户名”约束,按Delete键。此时头像四周出现红线,且IB提示约束错误(出现红色箭头按钮),这是由于头像视图缺少约束,我们现在就把新的约束补上。在左侧界面层次图中,选中头像head,按住Control键拖拽到其父视图View上,在弹出窗口中按住Shift键选中“Center Vertically in Container”,按住Shift键和Option键选中“Leading Space to Container”,最后单击“Add Constraints”添加这两个约束。

这里写图片描述

在右侧的尺寸审查窗口中,点击“Leading Space to: Superview”右侧的Edit按钮,在常量值处输入60。

这里写图片描述

IB设计器中出现了多条黄线,说明需要按照新的约束刷新界面,选中头像并按下快捷键⌥⌘=(也可以按下“Resolve Auto Layout Issues”按钮,在弹出菜单中选中“Update Frames”)刷新界面。

下一步是处理文本框。选中用户名文本框,可以看到它与其父视图垂直居中。现在我们希望让密码文本框垂直居中,因此选中用户名文本框,单击其“Align Center Y to: Superview”按Delete删除之。选中密码文本框,添加垂直居中其父视图的约束,可以单击Align按钮,在弹出窗口中选中“Vertically in Container”,值为0,Update Frames下拉菜单中可以选中“All Frames in Container”这样就会在添加约束后自动刷新界面,最后点击“Add 1 Constraint”按钮。

这里写图片描述

我们发现头像和文本框之间的间距太近了,可以让文本框和登录按钮距离右侧60。选中用户名文本框,单击“Align Center X to: Superview”按下Delete删除之,添加其右侧距离其父视图右侧60的约束,可以单击Pin按钮,在弹出窗口中不选“Constrain to margins”选项,在上方右侧文本框中输入60使其工字型标记点亮,Update Frames下拉菜单中选中“All Frames in Container” ,点击“Add 1 Constrinat”按钮。

这里写图片描述

好了,就是这么简单!赶紧使用Storyboard预览工具,或者使用iPhone和iPad模拟器运行一下吧。

iPad竖屏

iPad横屏

iPhone 4s竖屏

iPhone 4s横屏

太神奇了,这究竟是怎么做到的?原因就是我们设计了一套通用的[*, *]布局,另外又特殊设计了另一套[*, -]布局。iPad横竖屏都是[+, +],匹配[*, *]但不匹配[*, -],因此取[*, *]的布局。iPhone竖屏为[-, +],匹配[*, *]但不匹配[*, -],因此也取[*, *]的布局。而iPhone横屏为[*, -],匹配[*, -]因此取[*, -]的布局(有些拗口请见谅)。刚才也提到了,自适应布局类似于类的继承,我们要把最通用的布局约束放在父布局中,而在子布局中仅需调整若干约束,剩下的都可以复用父布局中的约束。在上面的例子中,我们都没有调整界面下方的文本控件的约束,即使是iPhone横屏时仍然取父布局中的约束。

回到Xcode的Main.storyboard中,选中头像,在右侧的尺寸审查窗口中的Constraints中有两个按钮“All”和“This Size Class”,“All”按钮用于显示所选对象在所有尺寸类型的约束,而“This Size Class”只显示所选对象在当前尺寸类型的约束。现在点击“This Size Class”按钮,可以看到头像只有2个约束,即垂直居中和左侧间距。点击“All”按钮后发现共有4个约束,其中两个浅色的是不在当前尺寸类型的约束。

这里写图片描述

双击浅色的“Align Center X to: 用户名”,注意到下图中的红圈部分。

这里写图片描述

这里说明的是当前约束是在[*, *]中激活,而在[*, -]中不激活。我们也可以点击红圈中的+号,添加其他尺寸类型并设置是否激活。点击红圈中的x号则会删掉当前约束在某一尺寸类型上的配置。同理,注意到上图中的Constant常量值前面也有一个+号,不难猜到也可以设置某个约束针对不同尺寸类型取不同的常量值,方法是一样的,这里就不再演示了。

在左侧界面层次窗口中,点开View的Constraints下拉列表,也可以看到很多浅色的约束,也是说明这些约束在当前尺寸类型中处于未激活的状态。注意,未激活并不等于删除,所以很多读者也包括我在使用新版Xcode的过程中遇到了这样的问题,就是我明明在IB中选中了某个约束并且按了Delete键,可是并没有删除这条约束。现在你应该知道为什么了吧。如果你真的想删掉某个约束,可以在IB中双击某个约束,然后在左侧的界面层次图中选中该约束并按Delete,这才会真正删除约束,而不是不激活。

三、总结

现在你已经成功帮助小李完成了他的任务,给你点个赞!程序最终的代码:

http://yunpan.cn/cLn2v73G3KsX9 (提取码:0071)

本篇文章只是对自适应布局做了最简单的介绍,在后续文章中咱们可以就这个话题进行更深入的探讨。如果你对我写的东西有任何建议、意见或者疑问,欢迎到我的CSDN博客留言:

http://blog.csdn.net/pucker

下期文章我们再见。

  • 9
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值