翻译@Auto Layout Guide(自动布局指南)
- 原文:Auto Layout Guide
- 作者:Apple
- 更新:Yannmm@Github.com
Getting Started(新手上路)
Working with Constraints in Interface Builder(在界面编辑器中使用约束)
(译者:界面编辑器,Interface Builder,以下简称IB🤔)
通过IB创建约束,有三种方式:按住control拖拽;使用固定和对齐工具(译者:Pin and Align Tools,其实是IB右下角的两个按钮,后者现在已经不叫Align,改为Add New Constraints);让IB自动设置,然后手动修改约束。以上方式各有所长,大多数人会选择一种熟练使用。但全部了解可以让我们更好的根据需要选择合适的方式。
不论使用哪种方式,都需要先将视图从对象库(Object Library)拖拽至画布,并调整其大小和位置。IB会自动创建一组原型约束(prototyping constraints),相对于画布左上角定义视图的尺寸和位置。
至此,app已经可以编译运行,展示界面。但随着开发的进行,要逐步添加显性约束。绝对不能发布使用原型约束的app。
一旦为视图添加自定义约束,所有原型约束会被自动移除。此时,由于缺少约束,布局歧义。受歧义影响的约束显示为红色,Xcode报警告。
莫慌,继续添加约束,直至布局完整。添加自定义约束时,必须做到有始有终。
更多关于修复警告和错误的信息,详见章节Debugging Auto Layout(调试自动布局)。
Control-Dragging Constraints(拖拽生成约束)
要在两个视图之间创建约束,可以按住control,将一个视图拖动至另一个。
松开鼠标,从弹出的灰色菜单中选择一个要添加的约束。
IB会根据约束的元素和拖拽的方向,生成可供选择的约束。如果沿水平方向拖拽,则可以设置水平间距,垂直对齐。如果沿垂直方向拖拽,则可以设置垂直间距,水平对齐。当然,还有许多其他可用约束(例如相对尺寸)。
注意
可拖拽的对象不仅有画布上的视图,也可以是场景对象列表(scene's document outline)中的图标。特别是画布上找不到的元素,如布局参照(layout guide)。拖拽的对象是后者时,可供选择的约束不再受拖拽方向影响。
IB根据视图当前frame创建约束。因此,添加约束前,要将其移动到合适的位置。一般来说,根据系统建议摆放视图(即视图在画布上移动时出现的蓝色虚线),能够得到符合预期的约束。况且,随时可以修改约束。
拖拽让我们迅速创建约束;然而,由于是根据当前frame创建,所以很容易产生偏差。要想更精确,可以在约束创建后逐一检查并修改,或使用固定和对齐工具创建约束。
更多关于拖拽创建约束的信息,详见自动布局帮助(Auto Layout Help)中同拖拽创建约束有关的内容。
Using the Stack, Align, Pin and Resolve Tools(堆叠,对齐,固定和问题解决工具的使用)
IB的右下角有四个布局工具:分别是堆叠,对齐,固定和问题解决。
想要更准确的创建约束,或一次创建多个约束,可以使用固定和对齐工具。无需提前排布视图;只需大致确定视图的位置,添加约束,勾选更新frame即可。系统会自动计算视图位置和尺寸,并在画布上更新。
Stack Tool(堆叠工具)
此工具可以快速创建堆叠视图(UIStackView)。选中画布上一个或多个视图,单击堆叠工具,这些视图会被嵌入一个新的堆叠视图中,其尺寸由内容决定。
注意
通过这种方式创建的堆叠视图方向和对齐方式取决于内容视图的相对位置。在属性面板可以修改各项属性的值。
Align Tool(对齐工具)
此工具可以快速对齐视图。选中要对齐的视图,点击对齐工具,有菜单弹出,包含所有可能的对齐方式。
选中一个或多个对齐方式,点击添加约束(Add Constraints)按钮。相应的对齐约束会被添加至视图。默认,这些约束没有任何偏移量(即直接对其),视图在画布上的尺寸和位置也不会自动更新。但是,添加约束之前,可以进行设置。
使用对齐工具时,一般选择两个或以上视图。然而,俯视图内水平对齐(Horizontally in Container)或父视图内垂直对齐(Vertically in Container)可以针对单个视图添加。可以同时选中多个对齐方式,一次添加多条约束,但很少需要这样做。
更多信息,详见自动布局帮助中使用固定和对齐工具添加约束的相关内容。
Pin Tool(固定工具)
此工具能够快速设置视图的相对位置和尺寸。选中要固定的视图,点击固定工具,有菜单弹出,包含若干选项。
菜单的上半部分用于——相对于四周的邻近元素——定义视图四边。对应的数字分别代当前间距;可以直接修改数字,也可以点击随后的三角形,选择系统推荐值,或更改参照元素。下方的"保留边距(Constrain to margins)"表示当参照元素是父视图时,相对边距还是相对四边约束。(译者:例如,相对于父视图的leading约束,如不勾选,则意味着superView.Leading;反之,则意味着superView.Leading.Margin。)
菜单的下半部分用于定义视图宽高。默认是当前frame值,可以修改。宽高比(Aspect Ratio)选项默认是当前frame宽高比,可以在添加约束后修改。
通常,我们一次只固定一个视图 。然而,选中两个或以上视图,可以约束它们等高或等宽。可以一次创建多条约束,可以自动更新视图frame。一旦确定要添加的约束,点击添加约束按钮(Add Constraints)即可。
更多信息,详见自动布局帮助中使用固定和对齐工具添加约束的相关内容。
Resolve Auto Layout Issues Tool(问题解决工具)
此工具可以解决许多常见布局问题。菜单的上部选项只影响选中视图,下部选项影响场景中所有元素。
借助此工具,我们可以根据视图约束更新其frame;也可以根据视图frame更新其约束。还可以添加缺失约束,清除约束,或者重置视图,替换为系统推荐约束。
添加或重置约束的操作在章节:Letting Interface Builder Create Constraints(让界面编辑器替我们创建约束)中有更详细的讨论。
Letting Interface Builder Create Constraints(界面编辑器自动创建约束)
IB可以自动创建部分,甚至所有约束:其基于视图当前frame推测出合适的约束。所以请注意视图的摆放——即使是微小的偏差,也可能导致截然不同的布局结果。
想要利用这一特性,点击问题解决工具,选择重置为推荐约束(Reset to Suggested Constraints)。IB会自动为选中视图(或所有视图)添加约束,产生明确,可满足的布局。
也可以自行添加一部分约束,然后点击问题解决工具,选择添加缺失约束(Add Missing Constraints)。系统会补全约束,类似的,适用于选中视图或所有视图。
这一特性让我们快速创建明确,可满足的布局。然而,除非界面足够简单,否则布局结果可能会和预想不同。所以,要多测试,确保结果符合预期。
Finding and Editing Constraints(查找和编辑约束)
添加约束后,还需要能够定位,查看,修改约束。访问约束的途径有很多,代表组织和呈现约束的不同方式。
Viewing Constraints in the Canvas(查看画布上的约束)
同选中视图相关的约束会被显示在画布上,用蓝色线条表示。线条形状,两端样式以及颜色能够透露许多关于约束的信息。
- 工型直线(两端为T型):代表空间长度。可以是视图宽高,也可以是两个视图的间距。
- 一般直线(两端无样式):代表四边对齐。例如,对齐两个视图的前边。另外,视图间距为0pt时,也用这种线条表示。
- 实线:代表"必要(Required)"约束(优先级等于1000)。
- 虚线:代表优先级小于1000的约束。
- 红线:代表受此约束影响的视图的布局有歧义,或无法满足。更多信息,可以通过问题导航面板或场景对象列表上方的箭头查看。
- 橙线:代表受此约束影响的视图的frame不正确。橙色虚线代表正确的frame。点击问题解决工具,选择更新frame(Update Frames)即可解决。
- 蓝线:代表受此约束影响的视图的布局明确,可满足,其frame正确。
- 等号标志:代表等高或等宽的约束。受影响的视图会相应的位置显示一条蓝色T型直线,中间有"="标志。
- 大于等于或小于等于标志:代表规定了大于等于或小于等于关系的约束,中间有">="或"<="标志。
Listing Constraints in the Document Outline(通过场景对象列表查看约束)
所有约束都可以在场景对象列表中找到,归类在所属视图下方:参与约束的两个元素的公共父视图,持有约束。据此,单个视图持有自身及其子视图,上下布局参照被根视图持有。
虽然约束散落在列表中,但大多数都被根视图持有。展开完整列表可以查看所有约束。
列表通过伪代码表示约束,内容较长,(约束之间)区分度不高,特别一个元素下多个约束并排显示时。可以调整列表宽度,显示完整信息。选中列表中的约束,画布上相应约束会高亮。
如果布局简单,通过场景对象列表查看约束的确非常方便。但随着布局复杂度增加,定位约束会变的非常困难。因此,最好以视图为单位查看约束:在画布上选中视图,查看相关约束;或通过尺寸面板查看相关约束。
Finding Constraints in the Size Inspector(通过尺寸面板中查看约束)
尺寸面板会显示所有同当前视图相关的约束。必要约束(优先级1000)表示为实线,可选约束表示为虚线。约束的相关信息在下方显示,罗列了参与的元素和属性;还可能包含关系,常量,系数及比例的信息。
上图中上方的图例展示了视图的哪些属性受约束影响。选中某个属性,下方列表会被过滤,只留下影响当前属性的约束。
更多信息,详见自动布局帮助中同查看元素约束有关的内容。
Examining and Editing Constraints(检查和编辑约束)
约束一旦被选中,属性面板会展示相关信息。例如关系等式的所有组成部分:元素,关系,常量,系数;另外还包括优先级和识别符等信息。
注意
identifier
属性允许我们为约束命名,从而在查看控制台信息和debug时更好的识别约束。
另外,可以将其标记为占位约束(Placeholder)。占位约束只在设计xib时有效。运行时的界面不包含这些约束,因此无效。举例来说,布局中有需要动态添加的约束,我们可以使用占位约束代表动态约束。这样,可以消除xib的布局警告,查看运行时的布局效果。
常量,优先级,系数,关系,识别符,是否占位,都可以直接修改;但参与约束的元素无法。元素的位置可以对调(当然系数和常量需要手动调整)。即可以修改元素属性,但不能更改元素。如果确实需要更改,请删除约束,重新添加。
也可以在视图的尺寸面板中修改约束。点击任意约束后的Edit(编辑)按钮,在弹出的菜单中修改关系,常量,优先级和系数。要做更多修改,双击约束,切换至约束属性面板。
更多信息,详见自动布局帮助中同编辑约束相关的内容。
Setting Content-Hugging and Compression-Resistance Priorities(内缩和外扩优先级)
要设置视图的内缩和外扩优先级(CHCR priorities),首先选中视图,然后打开尺寸面板,找到内缩和外扩优先级设置。
还可以设置视图的占位(Placeholder)固有尺寸。默认,系统使用intrinsicContentSize的返回值,作为视图固有尺寸。然而,如果固有尺寸能够在设计xib时确定,则可以设置一个占位值。这个尺寸只在设计xib时有效,运行时无效。
更多信息,详见自动布局帮助中同设置视图占位固有尺寸相关的内容。
iOS-Only Features(iOS独有特性)
iOS有一些针对自动布局的独有特性,包括布局参照(layout guide),视图留白(layout margin),可读内容参照(readable content guide),以及语义内容(semantic content)。
Top and Bottom Layout Guides(上下布局参照)
布局参照代表当前控制器可视范围的上下边界。例如,要避免视图被透明或半透明的栏位阻挡(UIKit bar,如status bar,navigtaion bar和tab bar),请相对布局参照约束。
布局参照遵守协议UILayoutSupport,其定义属性length
,用于表示布局参照到视图上下边缘的距离。特别注意:
- 对于上方布局参照(top layout guide),其代表控制器视图上边与所有上方栏位的最下边的距离,单位pt。(例如,上方有状态栏,然后是导航栏,则此时length = 状态栏高度 + 导航栏高度)
- 对于下方布局参照(bottom layout guide),其代表控制器视图下边与下方栏位的最上边的距离,单位pt。(例如,下方有tab bar,则此时length = tabBar.height)
布局参照可以参与约束,定义视图的上下及高度。通常,相对于上方布局参照的下边,或下方布局参照的上边约束视图。另外,代码创建约束时,还可以使用属性topAnchor
,bottomAnchor
及heightAnchor
。
相对根视图(root view)的上下边创建约束时,布局参照会自动作为备选项出现。如果它们是视图的最邻近元素,则默认选中。使用固定工具(Pin tool)时,通过点击相应位置的三角形,可以在视图边缘和布局参照之间切换。
Layout Margins(布局留白)
自动布局中,视图有留白(margin)。所谓留白,就是视图子视图与其边缘之间的推荐距离。通过访问视图属性layoutMargins或layoutMarginsGuide获取相关信息。属性layoutMargins
通过结构体UIEdgeInsets定义留白。只读属性layoutMarginsGuide
通过对象UILayoutGuide给出留白。此外,可以通过视图属性preservesSuperviewLayoutMargins规定视图留白和父视图留白之间的关系。
视图四周留白默认为8pt。当然,可以根据需要修改。
注意
系统自动设置和管理控制器根视图的留白。其上下留白为0pt,便于将内容置于栏位(UIKit bar)下方(如有)。两侧留白根据控制器的呈现方式变化,一般为16或20pt。以上数值无法修改。
相对于父视图约束时,一般参照其留白,而非边缘。UIKit中的枚举NSLayoutAttribute包含一组枚举值,分别代表各个方向留白:上下,左右,前后;以及基于留白的水平和垂直中心。
在IB中,通过control拖拽生成的,相对于父视图的约束默认参照留白。使用固定工具创建约束时,勾选"Constrain to marings",可以在留白和边缘之间切换。类似的,在属性面板中编辑约束时,在元素的下拉菜单中勾选"Relative to margin(相对留白)";其作用和"Constrain to margins"一致。
最后,代码创建约束时,使用父视图属性layoutMarginsGuide
。其包含一组锚点,相对这些锚点创建约束,代码可读性更好。
Readable Content Guides(可读内容参照)
视图属性readableContentGuide定义视图中文本对象的最适宜宽度。理想情况下,用户无需转动头部,即可阅读全部内容。
这个区域相对四边留白居中,且绝不会超出四边留白。其尺寸会根据系统动态字体的大小调整。如果字体变大,则尺寸变宽;因为用户在阅读时,与设备的距离更远。
可以在IB中设置视图留白等同于布局留白,还是可读内容参照。选中视图(此时一般是控制器根视图),打开尺寸面板,勾选"Follow Readable Width"。如此一来,任何相对于留白的约束,就是相对于可读内容参照。
注意
对于大多数设备来说,无论谁扮演视图留白(布局留白或可读内容参照),区别都很小,甚至没有。只有在横屏下的iPad,才能看出明显的区别。
Semantic Content(语义内容)
使用前后(leading和trailing)布局时,视图内容方向会自动根据语言的阅读方向调整(例如英语的阅读方向是从左至右,而阿拉伯语从右至左)。然而,某些视图无须变化,如模拟方位的按钮(上,下,左,右)。
视图属性semanticContentAttribute规定内容是否需要根据语言的阅读调整。
点击视图属性面板中的"Semantic",弹出菜单,选项如下:"Unspecified"表示总是反转;"Spatial(空间内容)","Playback(播放器)",和"Force Left-to-Right(强制从左至右)",表示前边等同于左边,后边等同于右边;"Force Right-to-Left",表示前边等同于右边,后边等同于左边。
Rules of Thumb(规则总结)
下述规则能够帮助我们顺利使用自动布局。当然,一切都有例外;但在违背规则之前,三思而后行。
不手动设置视图的frame,bounds及center。
-
尽量使用堆叠视图
堆叠视图能够自动管理其布局,免除了手动添加约束的麻烦。除非无法满足需求,否则不手动布局。
-
参照最邻近元素设置约束。
假设有两个相邻按钮A和B,约束B的前边时,参照A的后边,而非父视图的前边(即跨过了A)。
-
避免固定宽高。
自动布局的优势在于动态响应变化,固定宽高意味着将放弃这种优势。然而,可以限定款高的最大值或最小值。
设置约束无从下手时,可尝试使用固定和对齐工具。虽然效率不如control拖拽,但可以明确构成约束的元素和数值,从而有助于我们理清思路。
-
小心使用自动更新frame。如果视图布局有歧义或冲突,则结果无法预期。视图有可能从画布上消失,其原因可以是宽或高为0,也可以是视图位于屏幕之外。
当然,frame更新后可以撤销。
-
为约束添命名,便于区分。
按钮或标签的名称就是其文本内容。对于其他视图来说,需要在身份面板中设置"label",或者在场景对象列表中双击视图,填入名称。
-
创建约束时,尽量使用前后(leading和trailing),而非左右(left和right)。
通过视图属性semanticContentAttribute(iOS)或userInterfaceLayoutDirection(OS X)定义前后。
-
iOS中,参照控制器根视图四边添加约束时,最好使用如下约束:
-
水平约束。对于大多数控件来说,紧贴留白布局即可(即偏移量为0pt)。系统会根据设备类型和控制器的呈现方式自动调整边距。
对于占据整个横向空间的文本视图来说,相对可读内容参照布局。
对于需要完全填充整个空间的视图来说(例如背景图片),相对前后布局。
-
垂直约束。如果视图需要延伸至栏位(UIKit bar)下方,相对上下留白布局。这是滚动视图(scroll view)的常见用法,因为其内容需要在栏位下方滚动。但注意,可能需要调整滚动视图属性contentInset和scrollIndicatorInsets,以确保内容的初始位置正确。
如果视图不需出现在栏位下方,则相对上下布局参照约束。
-
代码布局时,必须将视图属性translatesAutoresizingMaskIntoConstraints置为
NO
。系统默认根据视图frame及其自动缩放掩码生成一组约束,添加至视图;这些约束很可能同自定义约束产生冲突。-
注意OS X和iOS计算布局的方式不同。
在OS X中,自动布局影响窗口的内容和尺寸。在iOS中,场景的尺寸和布局不可更改,自动布局仅影响场景的内容。
上述区别看似无关紧要,但对于布局设计,特别是约束的优先级顺序,有着深远的影响。