第4章介绍过,视图的frame属性定义了视图的大小和相对于父视图的位置。之前的章节中,无论是编写代码还是在Interface Builder中设置,frame都是基于绝对坐标(absolute coordinates)的。绝对坐标降低了界面布局的灵活性,它要求开发者事先知道屏幕的具体尺寸,无法自动适配不同尺寸的屏幕。
相反,自动布局描述的是视图之间的相对位置关系,它可以在应用运行时根据设备的屏幕尺寸动态改变各个视图的frame。
表15-1列出了不同设备的屏幕尺寸。(请读者注意,对于自动布局系统来说,Retina屏幕的尺寸与非Retina屏幕的尺寸是“相同”的,因为视图的坐标单位使用的是点而不是像素,虽然Retina屏幕的像素数目是非Retina屏幕的4倍,但是两者的点数目是相同的。)
表15-1 设备的屏幕尺寸
对齐矩形和布局属性
自动布局系统为视图定义了一个新的矩形区域——对齐矩形(alignment rectangle),该矩形有若干布局属性(见图15-5)。
图15-5 布局属性定义了视图的对齐矩形
XIB文件默认会使用自动布局系统,并为包含的所有视图都定义一个对齐矩形。但是,对于复杂的界面布局,例如Homepwner的详细界面,默认的显示效果通常并不好,需要进一步设置各个视图的布局方式。
对于开发者来说,由于事先不知道屏幕尺寸,因此无法直接为视图定义对齐矩形。相反,只需要为视图设置一系列约束(constraints),系统就可以确定所有布局属性的值,并根据布局属性为视图定义对齐矩形。
约束
约束既可以定义布局属性的具体值,也可以定义布局属性之间的关系。例如,可以为视图添加如下约束:“视图的高度始终是44点”“两个视图之间的垂直距离是8点”“这些视图的宽度始终保持相等”等。
虽然视图的布局属性很多,但是并不需要为每一个布局属性都添加一个约束。自动布局系统会根据约束和布局属性之间的关系计算出每一个布局属性的值,例如,如果为视图定义了左边和宽度约束,那么系统会自动计算出右边:左边+宽度=右边。
如果系统在检查了所有约束之后,仍然无法确定某些布局属性的值,就会提示“缺少约束(Missing Constraints)”或“有歧义的布局(Ambiguous Layout)”。如果在这种情况下构建应用,Xcode并不会报错,但是在应用运行时,系统将无法正确对界面布局。本章稍后会介绍如何调试这类布局错误。
下面就来练习为BNRDetailViewController中的UIToolbar对象添加约束,首先需要考虑UIToolbar对象在不同屏幕中的布局方式:
•UIToolbar对象应该始终位于屏幕底部。
•UIToolbar对象的宽度应该和屏幕宽度保持相等。
•UIToolbar对象的高度始终是44点。(Apple建议的UIToolbar标准高度。)
为了将期望的布局方式转换为约束,需要引入最近相邻视图(nearest neighbor)的概念。最近相邻视图通常是指视图在某个方向上距离最近的兄弟视图(视图层次结构中的同级视图),但是,如果视图在该方向上没有兄弟视图,那么最近相邻视图就是其父视图(见图15-6)。
图15-6 最近相邻视图
根据最近相邻视图的概念,可以将UIToolbar对象的布局方式转换为以下约束:
(1)UIToolbar对象的底边与其最近相邻视图(BNRDetailViewController的view,也是UIToolbar对象的父视图)的距离始终是0点。
(2)UIToolbar对象的左边与其最近相邻视图的距离始终是0点。
(3)UIToolbar对象的右边与其最近相邻视图的距离始终是0点。
(4)UIToolbar对象的高度始终是44点。
读者可能会发现,以上约束并没有包括UIToolbar对象的宽度和顶边。实际上,第2条和第3条约束已经限定了UIToolbar对象的宽度与屏幕宽度始终保持相等,而第1条和第4条约束则限定了UIToolbar对象的顶边与其最近相邻视图的距离始终是屏幕高度减去44点。
现在可以为UIToolbar对象添加约束了,与添加视图的过程类似,可以通过Interface Builder或编写代码为视图添加约束。Apple建议开发者尽可能使用Interface Builder,本章也将介绍如何在Interface Builder中添加约束。如果读者需要通过代码创建视图,可以参考本书第16章如何通过代码为视图添加约束。