原文:Adaptive Layout Tutorial in iOS 11: Getting Started
作者:József Vesza
译者:kmyhy更新说明:本教程由 József Vesza 升级至 iOS 11、Xcode 9 和 Swift 4。原文作者是 Sam Davies。
对于 iOS app 设计人员来说,自适应布局的出现导致了巨大的变化。在设计你的 app,你现在只需要创建一个布局就能在所有设备上运行——不需要添加复杂的平台相关代码!
本教程介绍自适应布局。你会学习 universal 故事板、size 类、布局和字体定制、以及 IB 的改进(而这会带给你很大的便利)。
我们将为一个简单的天气 app 创建 UI —— 我们将从头开始创建。如果你不是一个自动布局爱好者,也毋庸担心;本教程第一部分会以 step-by-step 方式介绍用自动布局构建 UI。你会惊讶地发现,你完成这个工作不需写一行代码!
Universal 故事板是你迈向自适应布局的第一步。一个故事板可同时用于 iPad 和 iPhone。无需为每种设备准备单独的故事板——这个过程不但单调无味,而且容易导致错误。
打开 Xcode,选择 File\New\Project…。
选择 iOS\Application\Single View App 然后点击 Next:
Product Name 设为 AdaptiveWeather,Language 设为 Swift。所有选项都不勾选,然后点击 Next:
然后指定项目保存路径,在项目导航器中,你会看到:
Main.storyboard 可以用于所有的设备类型,不管屏幕尺寸多大。打开该故事板你会看到它有一个 view controller,当前尺寸为 iPhone7 的屏幕大小:
在 File 面板中,有个 Use Trait Variations 选项,允许在项目中开启这个新特性。打开故事板的文件面板,你会看到这个选项是这个样子的:
对于新建的 iOS 项目默认开启这个选项。你可以在将老项目的故事板转换到新项目中时,手动打开这个选项。
打开 Main.storyboard ,拖一个 Image View 到画布上。打开 Size 面板,将 x 设置为 37,y 设置为 20。width 设置为 300,Height 设置为 265。
然后拖一个 UIView 到 image view 下方。打开 Size 面板,将 x 设置为 37,y 设置为340,Width 设为 300,Height 设为 265:
选中这个 view,打开 Identity 面板,将 Label 设为 TextContainer。注意 Document 面板可能未显示——如果这样的话,点 Show 按钮显示它。 这给 view 取了一个名字,方便我们在 Document 窗口中找到它。这个 view 最终用于容纳城市和气温 label。
当我们从 Object Library 将视图拖到故事板中时,因为它们默认背景色都是白色,和 view controller 的 view 的颜色一样,会导致我们很难看得清它们的位置。要解决这个问题,先选中 view controller 的 view,打开属性面板,设置 background color 为 #4AABF7。
然后,选择 TextContainer ,将它的背景色设为 #3780BA。
最终 view controller 可能是这个样子:
这两个 view 现在直接作为 view controller 的 subview,接下来我们要为它们添加一些约束。
选中 image view 点击自动布局工具栏中的 Align。勾上 Horizontally in Container,确保值设为 0,然后点 Add 1 Constraint。
点击 Add New Constraints 按钮,添加上边距为 20:
点击 Add 1 Constraint 按钮。
前面添加的约束让 image view 顶部间距固定,并水平居中对齐。现在需要设置 image View 和 TextContainer 的间距。右键,从 image view 拖到 TextContainer,像这样:
将会弹出一个约束上下文菜单。选择 Vertical Spacing:
这个约束限制了 image view 底部到 TextContainer 顶部之间的间距。
选中 image view ,打开 Size 面板,你会看到:
你会看到有 3 个约束,你可以在这个 size 面板修改每个约束。点击 Bottom Space To:TextContainer 约束旁边的 Edit 按钮,你会看见一个对话框弹出,你可以修改约束的属性。将 Constant 设置为 20:
点击对话框外的地方,关闭对话框。
我们将 TextContainer 和 image view 之间的间距设置为 20,但我们还需要为这个视图的其它 3 边指定约束。
选中 TextContainer 点击 Add New Constraints 按钮。在 Sapcing to nearest neighbour 处,将相对于 superview 的左、右、下间距都设置为 0。 确认 Constrain to margins 未勾选,这会将 view 周围的内白去掉。
你可以参考下图所示的对话框:
点击 Add 3 Constraints,添加新的约束。这会将 TextContainer 的左边、右边和底边和 view controller 的 view 对齐。
现在,你的故事板看起来是这个样子:
注意有一个约束显示为橙色,而另一个约束显示为红色。这说明你的布局中存在问题。你可以让故事板自动修改视图的 frame 以满足这些约束。但如果你真的这样做,你的 image view 会被压缩为 0,0 大小。
这是因为 Image View 中没有内容——也就是说它当前的 intrinsic height/width 都是 0。如果没有为自动布局提供正确的宽高约束,那么视图的宽和高将由 intrisinc size 决定。
在项目导航器中,打开 Assets.xcassets。下载 cloud_images.zip 并解压缩。你将找到 3 个文件。将 3 个文件从 Finder 拖到 asset catalog 右手边的空白区域:
这将新建一个 image set,并自动将图片填充到正确的位置:
现在,你可以设置 image view 的 image 属性了。回到 Main.storyboard ,选中 image view。打开属性面板,将 Image 属性设为 cloud_small,将 Content Mode 设为 Aspect Fit :
你的故事板现在变成这个样子:
图片有了,好像每件东西都显示正常;view controller 自动对视图进行了重新布局,以适配新加的约束。
通常,我们会在各种模拟器版本下运行 app——包括横屏和竖屏——以便测试新的 Universal 故事板。这个过程非常繁琐,但 Xcode 9 给我们一种更好的选择,就是新的 trait vairation 预览。
打开 Main.storyboard,找到画布下方的 View 按钮,点击它。这会弹出一个 trait 选择菜单:
在 Devices 这栏,选择 iPhone 4s(最右边的按钮)。
你会发现画布发生了变化,变成了 4 英寸屏:
要查看横屏效果,点击 trait 选择器中 Orientation 栏的 Landscape 按钮:
在调用不同模拟器方面,这是一种巨大的改进:通过点击按钮,你可以查看你的布局在不同设备上是否仍然显示。
注意到没有,在预览 iPhone 横屏时是不是有点不对劲?这是正常的——cloud 图片实在是太大李。。要解决这个问题,你需要给 image view 添加新约束。
回到故事板。右键,从 image view 拖到 view controller 的 view,新建一个约束。在上下文菜单中,选择 Equal Heights:
现在故事板中有好多约束都变红了。这是因为刚加的约束和已有约束发生了冲突,因为 image view 的高度不可能在和 view controller 的 view 相等的情况下,还保持之前的垂直边距对齐。
在 Document Outline 中选择新加的约束,打开属性面板,如果 First Item 不显示为 cloud_small.height,则请点击 First Item 下拉菜单中的 Reverse First and Second Item。
然后,将 Relation 设置为 Less Than or Equal,Multiplier 设为 0.4:
这是说,要么 cloud 图片的大小要么是图片的 intrinsic size,要么比这更小,为屏幕高度的 40%。
当你修改完约束,你会发现画布会自动刷新,变成:
完胜!
因为这是一个天气 app,你需要添加一些 Label 用于显示城市名称和当前气温。
在 Main.storyboard 中,返回 iPhone 7 竖屏,拖两个 Label 到 TextContainer 中,将它们按下图放置:
选择最上边的 label,点击 Align 和 Add New Constraints 按钮,让它水平居中,顶部边距 10:
然后,打开属性面板,将 Text 设为 Cupertino,Color 设为白色,字体设为: System,细,字号 150。
你可能发现 Text 当前是不完整的,这是因为 label 的 frame ——等会就解决它。
选中另一个 label,用 Align 和 Pin 菜单,让它水平居中,底部边距 10。在 size 面板中显示如下:
在属性面板中,将Text 设置为 28C,颜色白色,字体:System,Thin,字号 250。
在故事板中,Label 伸出了屏幕外边,并交叠起来了,这不是我们想要的。在解决这个问题之前,我们先来看一看 Trait。iPad Pro 9.7 吋屏下看起来效果倒是挺不错的:
当然,对于 iPhone 来说,这个字号未免大了一些:
在下一节,我们会来解决这些问题。
Universal 故事板是强大的——但你可能已经发行了,要在单一布局中适配所有屏幕是一个巨大的挑战。因此,自适应布局中另外几个工具将会非常有用。
自适应布局中的一个核心概念是尺码(size classes)。一个尺码是一种正确的用法,包含一定内容的 view 和 view controller 都能在给定的水平和垂直维度上进行显示。
Xcode 提供了两种尺码:Regular 和 Compact。尽管它们是相对于视图的物理空间而言,但也用于表示 view 的语义大小。
下表显示了不同设备和屏幕方向上的尺码定义:
这些尺码从设备传递到 app。但是,你可以在视图树的任意一级覆盖这些尺码。这在把 View controller 用在一个明显小于屏幕的容器中时候会非常有用。
尺码对于你和 app 设计中有什么意义?尽管你的 app 对尺码敏感,但你所构建的布局却没有明确的尺码——也就是说,你的布局保持相同的尺码。
在自适应布局的设计阶段,这就是关键。你应该构建一个基本布局,然后根据每个尺码进行定制。不要将每个尺码看成是完全独立的设计。将自适应布局看成一个层级结构,你可以将所有共享的设计放到父级,然后在子级的尺码中进行修改。
几乎不需要特别关注某种设备的布局设置。因为自适应布局的一个核心概念就是尺码和设备相关特性是分离的。也就是说支持自适应布局的 View 既可以用在一个全屏的 view controller 中,也可以用在一个嵌入的 view controller中,虽然外观上不同。
苹果的这种设计带来了一个好处,当苹果对设备的种类和特性进行扩展时,开发者和设计人员无需对 app 进行重构。
你可以利用尺码来定制 iPhone 的横屏布局,因为当前布局不太满足垂直空间有限的情况。
要使用 trait variations(特性变异),首先确认你的选择了一种高度为 Compact 的配置(例如 iPhone SE 横屏),然后点击 Trait 选择器右边的 Vary for Traits 按钮。
这里,你可以选择一个尺码,进行配置,并根据宽、高进行变异:
注意:这里的说法有点不一致。尺码总是和水平或垂直关联的。但是 IB 上使用的却是宽(width)和高(height)。这有一个简单的等意(宽=水平;高=垂直);它们是同一个概念。
当前布局在高度为 compact 时不正确。要解决这个,在 Vary for Traits 菜单中选择 Height:
你会发现底部的 bar 用蓝色进行了加亮。这表明,你当前正在某种特定的尺码布局下工作。
为了修改布局,你需要临时修改几个约束。用自动布局的术语来说,就是安装和卸载约束。如果在指定的尺码中,一个约束正在处于生效的状态,则叫做已安装,否则叫做已卸载,它不会发生作用。
选中 image view,打开 size 面板。你可以看一下哪些约束正在影响当前视图:
选中 Align Center X to: Superview 约束,单击它,按下 Delete 键从当前尺码中卸载该约束。这个约束会从故事版中小时,然后在 Document Outline 和 size 面板中,这条约束都会变灰。
注意:要看见已经卸载的约束,必须在 size 面板中将 This Size Class 切换到 All。
在 size 面板中双击已卸载的约束以选中该约束。在最下面会多出一行,如下图所示:
这表明这个约束在基础布局中是已安装,但在高度为紧凑(Compact Height)的布局中(也就是当前正在编辑的布局)是未安装的。重复同样步骤,将 image view 其它 3 个约束卸载。在 Document Outline 和 size 面板中会是这个样子:
然后来为当前尺码添加所需的约束。用 Algin 和 Pin 菜单,添加垂直居中约束,并设置左边距为 10:
右键,从 image view 拖到 view controller 的 view,然后选择 Equal Widths。
打开image view 的 size 面板,双击 Equal Width to: Super view 约束。如果 First Item 不为 cloud_small.Width,则点击下拉列表中的 Reverse First and Second Item。然后将 Multiplier设为 0.45。
image view 在所有尺码下的约束都设置完了,但 TextContainer 还需要来看一看。我们需要修改它在当前尺码下的约束,将 label 移动到右边。
TextContainer 有几个内部约束,指定了 Label 的位置,它们原本工作得很好。但是,另外还有 3个外部约束——左、右、底边距——不正确。将这个视图的右下边对齐到父视图,首先需要卸载左边距约束。
在 Document Outline 中选择 TextContainer,将 Leading Space 约束从 size 模板中卸载。为了检查这个约束在 Compact Height 尺码中确实已经卸载,请在 Document Outline 中选择它,然后看一眼 size 面板:
现在添加两个约束到 TextContainer,让它的位置摆放正确。这个视图的高等于 superview 的 1/2 高,以及让它的上边距对齐。
理论上,你可以和之前一样,右键,从 TextContainer 拖到 view controller 的 view。但在实践中,我们经常很难选到这个视图,因为它上面还有其他内容。有一个简单的方法,你可以在 Document Outline 中完成同样的工作。
在 Document Outline 中,右键,从 TextContainer 拖到 view controller 的 view:
按住 Shift 键,连续选中 Vertical Spacing to Top Layout Guide 和 Equal Width。点击 Add Constraints:
打开 TextContainer 的 size 面板,将这两个新约束修改为:
故事板刷新后是这个样子:
布局的修改完成了;大功即将告成。还有一些问题,是调整字体大小——我们放在下一节进行。
在 TextContainer 中,当前字体在 iPad 常规尺码上显示效果非常好,但对于紧凑尺码这个字体太大了。别担心——我们也可以在自己的尺码中修改字体大小。
注意:和布局的重定义不同,字体改变只会在基本布局上进行。改变字体不需要在 IB 中覆盖当前尺码的字体设置。它用的是后面所说的办法。
点击 Trait 选择器菜单中的 Down Varying 按钮。这个 bar 会变成灰色,表示你回到了基本布局。
选中 Cupertino 标签,打开属性面板。点击 Font 左边的 + 。
这会显示一个菜单,显示一个尺码组合的列表供你选择。选择 Width 为 Compact,Height 为 Any:
这会增加第二个字体选择框,并显示出是那个尺码所用。修改字号大小为 90:
然后,选择气温标签,重复同上步骤,这次将 Compact width 和 Any Height 尺码下的字号改为 150。
修改生效后的 IB 显示为这个样子:
好,看起来好多了,但 Cupertino 标签被剪切了。你可以捣鼓一下字号直到它完美,但这种方法一点也不灵活。Cupertino 是有点长,但 Washington,D.C. 更长—— Kleinfeltersville,PA 更更长!这个怎么弄?
再一次,自动布局来了。你只需要将两个 Label 的宽度限制在 TextContainer 的宽度就行了。右键,从Cupertino 标签拖到 TextContainer,然后选择 Equal Widths。
在气温标签上重复同样步骤。画布刷新后是这个样子:
呃,文本截断其实不是你想要的。这是 Label 在内容太长超出有效空间之后的默认行为。当然,有一种方法可以自动根据内容调整字号大小。
选中 Cupertino 标签,打开属性面板。将 AutoShrink 修改为 Minimum Font Scale 然后将值设为 0.5。同时将文本对齐设置为居中。属性面板中是这个样子:
对气温 label 重复同样步骤。
看一下 IB 中的画布,iPhone 布局现在看起来会好一点:
在预览编辑器中看起来不错,但是是时候 Build & run 来看看是否正确了。iPhone 屏幕看起来在每种尺码上都正确了:
恭喜你,你已经学习了基本的自适应布局技术!
完成后的示例项目从这里下载。
花点时间看一下你编写的这个 app(或者下载下来的完成后的项目)。它所特别的地方在于,它只用李一个故事版文件,无论在上面设备、什么屏幕方向上都能正常显示。
自适应布局必然是大势之所趋。其中最能说服人的一点莫过于,这种布局哪怕是对于未来的设备也能够显示正确,而不需要发布新的版本。
本教程最终得出结论,作为一个开发者,你需要改变 app 设计的方式。和使用基于像素方式的布局不同,你应该考虑 UI 元素和屏幕之间的关系。
如果你想学习更多关于 Adaptive Layout 的内容,请阅读我们的自适应布局视频教程系列,它会带你从初学者变成自适应布局的高手!你也可以看看这两个 WWDC 2016 “构建自适应 app” 的视频:Part 1,和 Part 2 。
同时,如果你有任何问题或建议,请在下面留言!