注:本文节选并翻译自Views with Intrinsic Content Size
动态高度的Label和Text Field
简单的Label和Text Field示例中,通过假定text field总是比name label更高简化了布局逻辑。可是,这不总是准确的。如果你增加label的字体大小到一定程度,它将会比text field更高。
本示例通过在运行期间高度最高的控件动态设置了所有控件的垂直间距。当使用标准系统字体是,本示例看起来与简单的Label和Text Field相同。可是,当你增加label的字体到36号时,布局的竖直间距会使用label的顶部距离重新计算。
这当然是一个人为设置的例子。毕竟,如果你增加了label字体大小,通常来说你还应该增加text field的字体大小。可是,在动态类型控件和指定尺寸控件(如图片)同时出现时,如果通过iPhone的辅助功能设置了超级超级大的字体时,此技术就可以证明是有效的。
视图和约束
就像简单的Label和Text Field中一样设置好视图层级,但是我们使用了稍微复杂一些的约束:
- Name Label.Leading = Superview.LeadingMargin
- Name Text Field.Trailing = Superview.TrailingMargin
- Name Text Field.Leading = Name Label.Trailing + Standard
- Name Label.Top >= Top Layout Guide.Bottom + 20.0
- Name Label.Top = Top Layout Guide.Bottom + 20.0 (Priority 249)
- Name Text Field.Top >= Top Layout Guide.Bottom + 20.0
- Name Text Field.Top = Top Layout Guide.Bottom + 20.0 (Priority 249)
- Name label.Baseline = Name Text Field.Baseline
属性
要让text field拉伸来填满可用空间,它的抗拉伸优先级必须低于label的。默认来说,Interface Builder会自动将label的抗拉伸优先级设置为251,text field的为250.你可以在尺寸检查器中验证这个数值。
名称 | Horizontal hugging | Vertical hugging | Horizontal resistance | Vertical resistance |
---|---|---|---|---|
Name Label | 251 | 251 | 750 | 750 |
Name Text Field | 250 | 250 | 750 | 750 |
讨论
本例中,对每一个控件都是用了一对约束。一个必须的且大于或等于的约束定义了控件自身和布局指导线之间的最小距离,同时,一个可选约束试图将此距离明确设置为20。
对于尺寸更高的控件,这两个约束全部满足,故系统会会在离布局指导线20点距离的地方放置该控件。可是,对于较矮的那个,只有最小距离约束可以满足。另一个约束会被忽略。这使得在运行期间,当控件的高度改变时,Auto Layout系统可以动态重新计算布局。
说明
确保将可选约束优先级的值设置得低于默认抗拉伸优先级(250)。否则,系统会破坏抗拉伸约束并拉伸视图而不是重新定位。
当使用基线定位进行布局时,某些地方可能会导致特别疑惑,因为只有当文字的相关视图以真实内容尺寸显示时,基线定位才是有效的。尽管包含了一个必要的基线约束,如果系统修改了其中一个视图的尺寸,文字可能就不会正常排列了。
等间距的两个按钮
在样式上看,这个例子和两个等宽按钮示例(见截图)基本相同。可是,在本例中,按钮的宽度基于带有最长标题的按钮。如果空间足够,所有按钮都会被拉伸直到他们同时满足最长按钮的真实内容尺寸。所有额外的空间会在按钮周围平均分配。
在iPhone上,两个等宽按钮和等间距的两个按钮在竖屏状态下的布局基本相同。只有当你把设备旋转到横屏方向时区别才会变得明显(或使用更大的设备,如iPad)。
视图和约束
在Interface Builder,拖拽并放置两个按钮和三个视图对象。把按钮放在视图中间,之后按照如下方式设置约束。
- Leading Dummy View.Leading = Superview.LeadingMargin
- Short Button.Leading = Leading Dummy View.Trailing
- Center Dummy View.Leading = Short Button.Trailing
- Long Button.Leading = Center Dummy View.Trailing
- Trailing Dummy View.Leading = Long Button.Trailing
- Trailing Dummy View.Trailing = Superview.TrailingMargin
- Bottom Layout Guide.Top = Leading Dummy View.Bottom + 20.0
- Bottom Layout Guide.Top = Short Button.Bottom + 20.0
- Bottom Layout Guide.Top = Center Dummy View.Bottom + 20.0
- Bottom Layout Guide.Top = Long Button.Bottom + 20.0
- Bottom Layout Guide.Top = Trailing Dummy View.Bottom + 20.0
- Short Button.Leading >= Superview.LeadingMargin
- Long Button.Leading >= Short Button.Trailing + Standard
- Superview.TrailingMargin >= Long Button.Trailing
- Leading Dummy View.Width = Center Dummy View.Width
- Leading Dummy View.Width = Trailing Dummy View.Width
- Short Button.Width = Long Button.Width
- Leading Dummy View.Height = 0.0
- Center Dummy View.Height = 0.0
- Trailing Dummy View.Height = 0.0
属性
给按钮设置一个可见的背景色,使在设备旋转时按钮的结构改变可以更容易看出来。此外,给按钮设置不同长度的标题。所有按钮应该基于最长标题来设置尺寸。
视图 | 属性 | 值 |
---|---|---|
Short Button | Background | Light Gray Color |
Short Button | Title | Short |
Long Button | Background | Light Gray Color |
Long Button | Title | Much Longer Button Title |
讨论
如图你看到的一样,这些约束已经变得复杂了。本例只是用来展示指定技术的,在真正的app里你应该考虑使用Stack View。
在本例中,你想要让空白区域随着父视图尺寸改变而改变。这意味着你需要一组等宽约束来控制空白区域的宽度;可是,你不能在空的地方直接设置约束。你可以设置尺寸约束的至少是一个对象。
在本例中,你用了几个虚拟视图来代表空白空间。这些视图是UIView类的空实例。在本例中,它们的高度被设置为0来最小化对视图层级的影响。
说明
虚拟视图会增加布局的性能损耗,所以你应该谨慎使用。如果这些视图非常大,其图形上下文会占用大量的内存,尽管它们没有包含任何有用信息。
此外,这些视图加入到了视图层级的响应者链中。这意味着它们会响应消息,如触摸检测,这些消息会沿着响应者链进行传递。如果没有小心处理,这些视图可能会打断并响应这些消息,产生难以排查的bug。
或者,你可以使用UILayoutGuide类实例来代表空白区域。这个轻量级的类使用一个矩形结构以便可以加入到Auto Layout的约束中。布局指导对象(layout guides)没有图形上下文,并且他们不会加入到视图层级中。这使布局指导对象对于分组项目或是定义空白区域来说特别合适。
不幸的是,你不能在Interface Builder的场景对象中添加布局指导对象,并且在基于story-board的场景中使用代码创建该对象会变得非常复杂。一般来说,使用StoryBoard和Interface Builder要比使用自定义布局指导对象更好。
本例使用了大于等于约束来设置按钮间的最小间距。“必须级”约束还确保了按钮一直拥有相同宽度,并且虚拟视图也都拥有相同宽度(虽然,他们与按钮的宽度不同)。剩下的约束通过按钮的CHCR优先级自行管理。如果空间不足,虚拟视图就会被忽略为0宽度,所有按钮会将虚拟视图的空间均分(在按钮之间使用基本间距)。一旦可用空间增加,所有按钮就会扩大直到达到最大按钮的真实内容尺寸为止,之后所有的虚拟视图也开始扩大。虚拟视图会持续扩大来填充所有可用空间。