原文:http://www.raywenderlich.com/113768/adaptive-layout-tutorial-in-ios-9-getting-started译者:@featherJ
【转载请写明出处,多谢!】
更新日期:2015/9/22:教程为iOS 9, Xcode 7 以及 Swift 2由Sam Davies更新。
自适应布局的介绍引发了ios开发者们的一个巨大的转变。现在,当你设计你的app的时候,你已经可以使用一个单独布局并且不添加任何设备适配的代码,使你的应用工作到所有的iOS设备上了!
这一片教程将为你介绍自适应布局。你将会学习到关于通用的storyboards,size classes,布局(layout)以及字体(font)自定义以及超有用的预览助手编辑器(Preview Assistant Editor)。
在本教程中,你将从头开始创建一个简单的天气应用的用户界面。如果你并不是特别热衷于自动布局也别担心,教程的第一部分将提供一步一步的讲解让你通过自动布局来创建一个用户界面。你将会惊奇的发现你可以不用写一行代码就完成这个界面。
通用的Storyboard
通用的Storyboard是你开始Adaptive Layout (自适应布局) 之旅的第一步。一个相同的Storyboard可以同时被用于iPad和iPhone设备。你并不需要为每一个设备存留一份Storyboard。
打开Xcode并且选择Create a new Xcode project,来创建一个新的Xcode项目:
选择 iOS -> Application -> Single View Application,然后点击下一步:
设置 Product Name(项目名) 为 AdaptiveWeather,Language(语言) 设置为Swift,同时设置Devices(设备) 为Universal(通用):
当项目开启之后,查看 Project Navigator(项目导航) 你讲会看到这个单独的storyboard文件:
Main.storyboard是一个为所有设备准备的单独的文件。打开这个storyboard,你将会看到他包含一个单独的 view controller(视图控制器),但是他的尺寸可能有些古怪:
好吧,他现在是个正方形!如果 Storyboard 在 Xcode 的编辑过程中匹配到某个目标设备的屏幕尺寸那就显然不太符合“一个Storyboard作用所有设备”的原则了,所以现在Storyboard是用一个抽象的尺寸替代的。
选择storyboard文件,打开File Inspector(文件检查器) ,你将可以看到use Size Classes的选项,在你的项目中,勾上它:
这个选项默认为所有新的iOS工程勾选。你可以在将旧项目升级到新项目的过程中手动激活这个勾选。
构建你的Storyboard
下面开始这个小节,打开Main.storyboard文件,从Object Library中拖出一个Image
View到你的view controller(视图控制器)画布中。在 Size Inspector
(尺寸检查器)中设置X的值为150,Y的值为20。同时设置Width(宽度)为300,Height(高度)为265。
下一步,从Object Library中拖出一个View放到Image View的下面。在 Size Inspector (尺寸检查器)中设置X为150,Y为315,同时设置Width(宽度)为300,Height(高度)为265。
选择你刚刚添加的View,打开Identity Inspector(身份检查器),编辑Document下的Label的内容为TextContainer:
因为刚刚拖出来的View和View
Controller(视图控制器)的背景色默认都为白色,所以可能不太容易找到这个View。下面我们修改一下他们的背景色。选择View
Controller控件,打开Attributes Inspector(属性查看器)设置Background(背景色)为#4AABF7。
(其实随便一个颜色就好,因为Xcode的颜色选择器没没法设置16进制颜色值)。
下一步选择选择TextContainer,设置它的Background(背景色)为#3780BA。
接下来你的视图控制器看上去将是下面截图的样纸:
Image View和TextContainer这两个控件是view controller的子项,下面我们将为他们设置布局约束。
添加布局约束
选择Image View并且点击在下方自动布局栏中的Align按钮,勾选中Horizontal Center in Container(容器中的水平中心),设置值为0,然后点击Add 1 Constraint(添加一个约束)。
接下来,点击Pin按钮,添加一个顶部距离最近控件的距离为0的约束,如下图:
你添加的这些约束将使得Image View有一个距离顶部和距离水平中心的固定距离。现在我们将设置 Image
View 和 TextContainer 之间的间距。在 Image
View上按住Control键并拖拽至TextContainer然后释放鼠标,如下图:
接下来将显示一个约束内容的菜单,选择Vertical Spacing(垂直间距):
这个约束用于决定从 Image View 控件的底部到TextContainer控件的顶部的距离。
选择你的 Image View 然后打开 Size Inspector(尺寸检查器) 你会看到如下:
你会看到你已经添加了三个约束到布局上了,你可以在Size
Inspector(尺寸检查器)内简单的配置内一个约束的参数。在Bottom Space To:
TextContainer约束上,点击Edit(编辑)按钮,将会弹出一个配置约束属性的对话框,设置Constant的值为20:
然后点击对话框外部,关闭这个对话框。
现在已经将Image View和TextContainer之间的间隔设置为了20点。现在你还需要设置TextContainer另外三个边的约束。
选择TextContainer然后点击底部栏的Pin图标,在Spacing to nearest
neighbor(约束到最近部件)部分,设置left,right和bottom的间隔为0。保持Constrain to
margins(约束到边缘)复选框为未选中状态,如果勾选这个复选框,将会移除掉视图的外边距。
作为参考,这个对话框应该是如下的样纸:
点击Add 3 constraints(添加3个约束)按钮将这些新的约束添加到TextContainer上。这样便设置好了TextContainer距离容器的左右下边距的约束。
接下来你的storyboard看上去应该是如同下面截图的样纸:
你应该已经注意到了在你的视图上有一些橘黄色的和一些红色的约束。这表示你的约束存在一些问题需要你注意一下。你可以通过storyboard的自动更新到frames的功能来调整这些约束,但是如果你现在这么做,Image View的尺寸将收缩为0。
这是因为现在这个Image View内部还没有任何内容,这意味着他的内部高度和宽度都为0。如果没有物理宽高的约束,自动布局是会依赖于部件的内部尺寸来决定他的宽度和高度的。
接下来,在Project Navigator(项目导航)中打开Images.xcassets文件。下载cloud_images.zip文件并解压,你将会发现里面有三个图片。 在Finder中选择这个三个图片,并且拖拽他们到空的资源目录中:
他们将创建一个新的图像集,并且已经分配好了这三个图像:
现在你可以应用你的图像集到Image View了,返回Main.storyboard 文件并选择Image
View。切换到Attributes Inspector(属性查看器),在Image输入框中输入cloud然后选择View -> Mode
为Aspect Fit (这几个选项试一下就知道大概是做什么的) 如下图:
接下来你的storyboard将看上去如下图这样:
但是目前仍有一些橘红色的约束,我们还有很多的事情要去做。 首先,在Document Outline(文档大纲中)选择View Controller的View:
然后点击在底栏中选择 Resolve Auto Layout Issues 图标按钮。然后在弹出的对话框中选择All Views -> Update Frames:
然后 View Controller 内的空间将会自动重新排列,以解决之前不太符合的约束。接下来你看到的storyboard将会是如下的样纸:
预览助手编辑器
通常你需要构建并在 iPad, iPhone 4s, 5s, 6 和 6
Plus设备上运行你的项目,并在每一个旋转方向上检查,以保证这个新的通用storyboard。这个过程是很麻烦的,但是Xcode6提供了一个更好
的选择即新的Preview Assistant Editor(预览助手编辑器)。
打开Main.storyboard文件,然后点击View -> Assistant Editor
-> Show Assistant Editor。这将编辑区分成两部分。在Jump
Bar中点击Automatic,然后在弹出的下拉菜单中选择Preview,然后选择Main.storyboard。
然后新的预览编辑器将呈现出一个storyboard在4英寸iPhone的屏幕的效果。如下:
通过点击预览框底部的 rotation icon(旋转图标)可以旋转预览效果。现在预览效果将旋转为横向:
这是一个模拟多设备的巨大的改进,但是,还有更多!点击预览编辑器左下角的+按钮将可以得到更多的可用预览:
在列表中选择5.5英寸的iPhone和iPad,以将他们添加到预览区中一起显示:
注意到在横向的iPhone中有什么奇怪的效果了么?是的,云图片实在太大了!为了修复这个问题,我们将为Image View加入一个新的约束。
返回storyboard,在Image View上按下Control并拖拽到View Controller的View中释放鼠标来创建一个新的约束,在弹出的菜单中选择Equal Heights(等高):
现在在storyboard中又出现了红色的约束。这是因为你刚刚添加的约束与现有的约束发生了冲突,当View Controller的View保持垂直方向的时候,不可能有相同的高度。
在 Document Outline (文档大纲) 中选择刚刚添加的约束,然后打开Attributes
Inspector (属性检查器)。如果第一项并没有被设置为cloud_small.Height,那么选择第一项的下拉菜单中的Reverse
First and Second Item选项。
然后,设置Relation为 Less Than or Equal,设置 Multiplier (乘数) 为0.40。如下图:
这意味着,云的图片将在是原始尺寸,或者当高度低于屏幕的40%的时候变小。
你会发现预览面板的效果会在你更新约束的时候自动刷新。效果如下:
那么此时是否可以让多个设备的预览效果同时自动更新呢?答案是肯定的,新的Preview Assistant Editor (预览助手编辑器)真的很有用!
要把现在的效果做成一个天气的app,你还需要添加一些标签,用于显示城市名和当前的温度。
为TextContainer添加内容
打开Main.storyboard文件,从Object Library中拖两个Label(文本标签)到TextContainer视图中,然后把他们排列成如下的样纸:
选择上面的一个标签然后通过Align和Pin菜单设置它的水平位置到中心,然后再添加一个 top spacing to nearest neighbor为10的约束,如下图所示:
下一步,选择 Attributes Inspector(属性检查器),设置Text (文本)为Cupertino,Color(颜色)为White(白色),然后设置font(字体)为System, Thin同时Size为150。
现在你可能会发现文字变得无法辨认,这是因为Label的框架的缘故,我们很快就可以解决这个问题了。
现在选择另一个Label,同时依旧通过Align和Pin菜单设置它的水平位置到中心,然后设置bottom space to nearest neighbor为10,检查Size Inspector(尺寸检查器),是否和下图一致:
通过Attributes Inspector(属性检查器),设置Text为28C,然后设置Color为White,字体为System, Thin并且字号为250。
现在你可以解决刚才我们提到的由于标签的框架导致的问题了。选择View Controller 的
view,点击storyboard底栏的Resolve Auto Layout Issues按钮,然后选择All Views ->
Update Frames。然后你将会看到storyboard更新为如下图:
现在你发现两个Label发生了重叠,看上去并不是我们所期待的样子。但是,在我们修复任何问题之前,可以发现在预览区中iPad版本看上去是好的:
可以预见的,iPhone的字号太大了:
我们将在下一个章节中修复这个尺寸的问题。
Size Classes
通用的storyboard是很给力的,但是已经发现为所有的设备创建一个通用的布局已经有些挑战性了。然而,Adaptive Layout (自适应布局)有更多的工具可以用来解决这些问题。
自适应布局的核心概念之一便是size classes。Size Class是一个可以应用于任何能够在横向或纵向进行显示的视图或者视图控制器的属性。
Xcode提供了两种Size Classe:Regular(正常的)和Compact(紧凑)。尽管他们与视图的物理尺寸是有关联的,但是他们仍然可以用一种语义上的尺寸呈现出来。
下面这个表格列出了size classe可以应用的不同的设备和方向:
这些都是设备可以应用的size classe。当然,你也可以在视图结构中的任意一部分中重写size classe。这在比屏幕要小的容器中是相当有用的。
Size Classes 和你
这对你和你所设计的app到底意味着什么呢?尽管你的app是有size class的,但是你构建的布局和size class是无关的,也就是说,你的布局需要为所有的size class进行调整。
在涉及到自适应布局的设计的时候,这是很重要的一点。首先你需要创建一个基本的布局,然后在为每一个特殊的size
class去调整你需要的size class。这里,不是要把每一个size
class视为一个全新的东西去从头设计。你可以把一个自适应布局,想象成是一个树状的层次结构,在这个层次中,你可以把所有通用的设计放在父级节点中,
然后把需要单独处理的部分放到子级的size class中。
目前为止,我们几乎还没有提到具体的设备布局适配问题。这是因为在自适应布局的核心概念中,size class是抽象于具体设备的。意思是说,一个支持自适应布局的视图,可以在一个完整的屏幕中显示,也可以被包含与另一个视图控制器中显示。
这样做也是有益于苹果的,这样即使设备的屏幕范围扩大了,也不需要迫使程序的开发者或者设计人员从新设计他们的app。
现在我们将使用size class来iPhone平放时候的布局,已解决目前的用户界面看起来不好看的问题。
使用Class Sizes
点击底栏的w Any h Any按钮,你将看见size class的选择器弹出:
在这里你可以通过选择表格单元的形式来选择用哪种size class来呈现。一共有九个可能的选项:你有三个纵向的选择和三中水平选择(any(任意),regular(正常),compact(紧凑)),那么总共有九种size class可供选择。
注意:这里在命名上有些轻微的差异。Size class
通常是和horizontal(水平)与vertical(垂直)联系起来的。但是,在这里却用了width和height,在这里width 等同于
horizontal,而height 等同于 vertical。
我们现在的布局还不能很好的工作于compact(紧凑)的高上。为了解决这个问题,我们将选择 Any(任何)Width | Compact(紧凑) Height的size class:
于是你将立刻发现在编辑器中的两个变化:
画布的形状变成了一个新的size class。
底栏变成了蓝色。这表示,你现在正在工作于一个特定的size class布局上。
为了改变布局,我们需要暂时的改变一些约束。在自适应布局中有这样两个术语installing(安装)和uninstalling(卸载)。一个约束被安装表示它处于激活状态了,反之,如果约束被卸载则表示它在当前的size class中不在起作用。
然后我们通过点击的方式选择Image View,打开Size Inspector(尺寸检查器)。你可以看到指定视图的所有约束:
通过单击的方式选择Align Center X to: Superview约束,然后按Delete键从当前的size class中卸载这个约束。这个约束边立即从当前的栏中消失了,同时在Document Outline(文档大纲)中变为了灰色:
注意:你可以在Size Inspector(尺寸检查器)中This Size Class选项切换为All来查看已经被卸载的约束。
在Size Inspector(尺寸检查器)中双击被卸载的约束,你可以看到在末尾显示的这部分:
这表示这个约束在基本布局中是被安装的,但是在Any Width | Compact Height布局中是没有被安装的。
接下来我们用同样的方式卸载掉image view上的其他三个约束。然后你的document outline(文档大纲) 和image view的Size Inspector(尺寸检查器)将会是如下的样纸:
现在我们将为当前的size class添加需要的约束。通过Align和Pin菜单来添加Vertically in the Container约束以及设置 left spacing to nearest neighbor为10:
通过 Control-拖拽 的方式从 image view拖到view controller的view中然后在弹出的菜单中选择Equal Widths。
打开image view的Size Inspector(尺寸检查器)然后双击 Equal Width to:
Superview约束。如果First Item 不是cloud_small.Width,你需要点击下拉菜单中的Reverse First
and Second Item。然后设置Multiplier为0.45。
现在,在image view 中已经为当前的size class设置好了约束,但是text container还是有问题的。现在我们需要通过约束的方式在使得在当前size class中文字部分变成居右。
现在TextContainer内部针对标签的约束看起来还是可以接受的。为了使得TextContainer居右,我们先要卸载掉居左的约束。
在Document outline(文档大纲)中选择居左的约束,标签为TextContainer.leader = leading:
然后通过Cmd-Delete的组合键来卸载掉这个约束。
现在我们需要添加两个新的约束到TextContainer,来使得他的位置保持正确。
下面我们可以直接在Document outline(文档大纲)中通过 Control-拖拽 的方式从TextContainer拖到view controller的View中:
然后通过 Shift-点击 的方式加选 Top Space to Top Layout Guide和 Equal Widths。然后点击Add Constraints(添加约束)来创建新的约束:
打开 TextContainer 的Size Inspector(尺寸检查器) 然后更新我们刚刚创建的两个约束为如下:
Top Space to: Top Layout Guide 的 Constant值设置为0。
Equal Width to: Superview 的 Multiplier 值设置为 0.5。 注意这里你可能需要翻转第一项和第二项,如果已经是正确的则不需要再次翻转。 通过双击的方式来进入这个约束进行设置即可。
点击底部的 Resolve Auto Layout Issues 图标,然后选择 All Views -> Update frames。接下来storyboard将更新成新布局的效果如下:
于是乎,我们的布局就修改完成啦,现在距离成品已经近在咫尺了。但是仍有一些字号问题需要解决,我们将在下一个章节中继续讲解。
适配字体
现在TextContainer中的字号在iPad设备上看上去还好,但是在compact(紧凑)的size class中就显得太大了。现在我们需要在size class中来重写字号。
注意:不想布局的重写,对于字体的配置将会影响到基本布局。对于字体的配置并不遵从当前的size class重写原则。我们需要用下面的方法来实现。
点击底部的size class按钮,然后通过表格选择当前的size class为基本的Any Width | Any Height。然后你的size class 变成为了之前我们创建的基本布局。
现在选择Cupertino字样的标签然后打开Attributes Inspector(属性检查器)。点击Font左侧的+号按钮:
在弹出的菜单中选择对应的size class 来重写字号。 选择Compact Width -> Any Height:
这个操作会创建第二个字体选择器,用于应用特殊的size class。现在更新新的选择器中的字号为90:
现在我们用同样的方式来更新温度的Label,这次设置Compact Width > Any Height的字号为150。
接下来在预览区中自动更新的效果如下:
现在,看上去似乎好了一些,但是Cupertino标签还是被裁减了。反复调整字号到适合的大小明显不是一个明智之
举。Cupertino稍微有点长了,但是Washington, D.C.会更长,Kleinfeltersville,
PA会更加的长!那么我们应该如何做呢?
我们又一次需要用自动布局来解决这个问题了。我们只需要简单的设置这两个文本标签的宽度与TextContainer相匹配就可以了。用 Control-过拽 的方式从Cupertino标签拖拽到TextContainer,然后选择Equal Widths。
同样对温度标签也这样做。于是显示的效果如下:
嗯,文本的截断并不是我们期望的。这是当文本比容器长时候的默认填充方式。现在我们需要一个方式来动态的调整字号到容器的尺寸。
选择Cupertino标签,然后打开Attributes Inspector(属性检查器)。改变
AutoShrink 为 Minimum font scale 然后设置值为0.5。与此同时,我们设置Text
Alignment为Centered。就像下面的样子:
我们对温度标签也做同样的处理。
现在再来看预览区,iPhone的布局看上去就非常好了:
在预览区中看上去还不错,但是我们可能还是可以花一些时间去看看是否每一样东西都是正常工作的。iPhone的屏幕看上去像是正确的样子:
恭喜,你已经学到了一些基本的自适应布局的知识了。