AutoLayout 是一种约束满足系统,就是“限制”意思。
约束类型#
布局约束(NSLayoutConstraint公有):布局约束的规则是用来指定视图的几何特这,这些规则要么通过视图与其他视图的关联确定视图的位置和尺寸,要么直接将视图的位置和大小设置成固定的值。
内容大小约束(NSLayoutSizeLayoutConstraint私有):内容大小的规则指定视图的尺寸与内容的关系,例如,内容吸附规则尽量避免添加补白,而内容压缩规则防止内容被剪切。
自动尺寸调整约束(NSAutoresizingMaskLayoutConstraint私有):自动尺寸调整约束将原来的自动尺寸调整掩码转换成Auto Layout系统中的对应约束。
布局支持约束(_UILayoutSupportConstraint私有):布局支持约束是iOS7.0中新增的约束,它用来建立视图控制器实例顶部和底部的实际边界。布局支持约束防止视图的内容与状态栏之类的障碍物重叠。
原型约束(NSIBPrototypingLayoutConstraint私有):原型约束也是iOS7中新增的约束,它是Iterface Builder(IB)为你添加的约束。使用原型约束在递增是构建界面的同时,仍然保留着一个用于测试的工作界面。
在上述的这些类中,虽然除了第一个类外,其余的都是私有类,但是所有这些类都可以通过公共接口API和IB创建。
优先级#
约束的优先级是表示Auto Layout考虑各个布局请求的强烈程度的数字。AutoLayout使用优先级来解决约束冲突,并决定优先级处理哪个规则。
在实际应用中,约束优先级是可读的,有时是可以设置的属性。优先级是从0到1000的浮点数。
冲突的优先级###
优先级为501的规则肯定比优先级为500的规则优先处理。
枚举型优先级###
内容大小约束#
每一个视图的框架由一个原点(视图所在的位置)和一个尺寸(视图在其俯视图中的宽度和高度)组成。虽然可以用明确的规则指出位置和大小,但是有时候却希望能够通过内容来确定大小,与内容相关的约束有两种:内容吸附和压缩阻力。这两种约束规则指出了AutoLayout感觉内容大小伸展,挤压或填充视图的难易程度。
不包含自然内容的视图的内容大小为(-1,-1)。UIKit将这种“无视图”的尺寸声明为UIViewNoIntrinsicMetric。OS X上的AppKit没有提供对应的声明。
内容吸附###
内容吸附约束限制视图允许自身伸展和填充视图的程度。如果内容吸附优先级较高,则将视图的框架与内在内容大小相匹配。如果内容大小比较小,则希望框架也比较小。拉力线向内拉向视图的自然边缘以抵抗填充。
压缩阻力###
压缩阻力约束防止防止视图剪切其内容。高压缩阻力优先级可以确保显示出的视图的完整内在内容。使用压缩阻力,压力线从内容的内部开始向外推,以确保整个内在内容都是呈现出来的。
压缩阻力将视图的大小与视图的内在内容相匹配,以防止视图剪切其内容。
构建布局约束###
NSLayoutAttributeLeft 左
NSLayoutAttributeRight 右
NSLayoutAttributeTop 上
NSLayoutAttributeBottom 下
NSLayoutAttributeLeading 前
NSLayoutAttributeTrailing 后
NSLayoutAttributeWidht 宽
NSLayoutAttributeHeight 高
NSLayoutAttributeCenterX 中心X
NSLayoutAttributeCenterY 中心Y
NSLayoutAttributeBaseLine 基线
NSLayoutAttributNotAnAttribute 占位
NSLayoutRelationLessThanOrEqual <=
NSLayoutRelationEqual =
NSLayoutRelationGreaterThanOrEqual >=
布局约束类###
数学规则通过构建NSLayoutConstraint类的实例来创建,然后将创建的规则添加到视图中。该例的实例提供了下面几个基本约束:
- priority 该属性存储约束的优先级值。AutoLayout系统通过对约束排序来选择响应哪个请求。
- fistItem 与 secondItem 这两个属性是指视图。约束可能仅涉及一个视图的属性,也可能涉及两个视图之间的关系。有效约束的第一项是非nil的。第二项可能是nil也可能不是。
- firstAttribute 与 secondAttribute 这两个属性是约束系统中的名词,他们描述视图的对齐矩形的特征,如左边,右边,中心和高度。这些属性值为上面构建布局约束值中的任意两个,如果不存在第二项,则把第二项设置为NSLayoutAttributeNotAnAttribute。
- releateion 关系是约束中的动词,他们指出属性之间如何相互比较,相等,大于等于,小于等于。约束的常量是个枚举值。
- multiplier 和 constant 这两个属性提供了代数元素,增强了约束系统的功能和灵活性。通过这两个属性,可以指出一个视图是另一个视图大小的一半,也可以指出一个视图是将其俯视图偏移一定的距离得到的。这俩个属性都是浮点型值,他们对应于构成约束的方程的m(乘数) 和 b(常数)元素。
约束数学###
不管约束是如何创建的,所有约束的本质都是相等或者不能关系。用公式表示如下:
y (关系) m * x + b
y 和 x 表示各种视图属性,如宽度,centerY或顶边。这里,m是常数缩放因子,b是常数偏移值。列如,可以说“视图B的左边应位于视图A的右边15点处”。其关系方程如下
视图B的左边 = 视图A的右边 + 15
在这个方程中,关系是相等关系,常数偏移值(b)是15,缩放因子或乘数(m)是1。在此处尽量防止上述方程看上去像代码,因为后面会讲到,在Objective-C中不用代码来约束声明。
约束不一定要严格使用相等关系。其实也可以使用不等关系。类如也可以说视图B的左边位于视图A的右边不小于15点处,其关系方程如下:
视图B的左边 >= 视图A的右边 + 15
偏移值用来在对象之间添加固定的间隙,乘数用来缩放视图。
第一项和第二项####
我们构建的每个带乘数(m)和常数(b)的这种关系方程总是适用于第二项:
firstItem.firstAttribute (R) secondItem.secondAttribute * m + b
没有绝对的规则规定那个视图必须是第一项,哪个视图必须是第二项。可以随心所欲的指定视图的顺序。例如可以这样规定:
视图B的左边 = 视图A的右边 + 10
也可以这样规定:
视图A的右边 = 视图B的左边 - 10
这两个方程基本上是同一个意思。
在第一个示例中,视图B是firstItem;在第二个示例中,视图B又成了secondItem。他们都描述了一个同样的布局:视图B出现在视图A的右边10点处。下面的示例以一种更加可视化的方式来呈现这两个意思相同的约束所描述的关系:
[视图A] -10- [视图B]
这种可视化格式将在后面继续讨论,
要使它像在第一个示例中那样,在数学上采用正数值,有一个小技巧,只要将上方,左边,前边的视图作为firstItem。后边,右边,下方视图作为secontItem。这样做略微有些违反直觉,因为许多人以为这是这是视图A的后边接上10点,后面再接上视图B的前边。
但是由于不能用NSLayoutContstraint合法的表示下面的公式:
视图A的后边 + 10 = 视图B的左边
因此要么尽量保留视图的顺序(使用负值,描述从secondeItem回到firstItem需移动多少),要么将产生的视图顺序倒过来,保持数值为正数。
创建布局的约束######
构建布局的约束有以下三种方法:
- 用IB设计界面。IB可以生成支持你的布局约束,你可以在可视化编辑器里面进行进一步定制约束。
- 用可视化格式语言描述约束,并允许NSLayoutConstraint类根据你的请求生成具体的实例(constraintsWithVisualFormat:options:metrice:Views:)。
- 为每个组件提供一个基本关系,从而构建NSLayoutCionstraint类的实例(constrianitWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:)。
从下向上构建约束可以使开发人员更好的控制,并且能够更好的诠释“最小惊奇原则”。要是界面和你的预期和设计一致,通常最好使用后两个方法中的一个,通过代码来手动构建约束。
遗憾的是,虽然在Xcode5中做了很大的改进,但是IB还是可能在几个层次中出现问题,它会在界面上到处分散约束引用,他没有办法组合记录功能上相关的约束,没有提供表达边界的边界器,而边界条件这是使用AutoLayout进行设计的重要的主题。
构建NSLayoutConstraint实例######
NSLayoutConstraint的类方法
constrainWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant: 每次只创建一个约束。每个布局约束定义一个规则,可能涉及一个视图,也可能涉及两个视图。
如果是涉及两个视图的规则,则上述创建的约束的方法产生一个严格的“视图.属性 R 视图.属性*乘数 + 常数”关系,其中R是等于(==),大于等于(>=)或者小于等于(<=)关系之一。
我们来看一下下面的示例:
[self.view addConstraint:[NSLayoutConstraintWithItem:self.view attribute:NSLayoutAttributeCentenX releatedBy:NSLayoutRelationEqual toItem:textField attribute:NSLayoutAttributeCenterX multiplier:1 constrant:0]];
该代码调用上述方法向一个视图控制器的视图self.view中添加了一个新的约束,要求将文本字段水平居中对齐。其实现原理是在两个视图的水平中心(NSLayoutAttributeCenterX属性)之间设置相等关系(NSLayoutReleationEqual)。这里的乘数是1,偏移值常量是0。用等式表示如下:
[self.view ]的centerX = ([文本字段]的centerX * 1) + 0
这个等式的消息的意思是:“请确保我的视图中心和文本字段中心的X坐标对齐”。
addConstraint:方法将该约束添加到视图中,与其他约束一起存储在constraints属性中。
一元约束######
不是所有的约束都引用两个视图。这些约束仅对一个视图进行操作,尤其是那些处理视图尺寸的约束。例如,指出视图的宽度为50 的约束不引用其他任何视图。
这些类型的约束是一元的,不涉及第二个视图。在这种情况下,secondItem属性将为nil,可以通过检查约束对此进行简单的测试。
if (constraint.secondIte == nil){
NSLog(@"Constraint is unary");
}
例如,可以建立一个将视图最小宽度设置为100点的一元约束。
NSLayoutConstraint * constraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribut multiplier:1 constant:100];
[view addConstraint:constraint];
该约束对应如下规则:
[view]的宽度 >=100
该约束的第二项是nil,其属性被设置为not an attribute。实际上,如果你不太在乎可读性的话,可以将第二个属性设置成你喜欢的任意的属性。如果上述代码仍然这样设置,约束仍然有效,因为没有第二项可以引用。
不含视图项的约束是不合法的######
每个约束至少要引用一个视图,比较常见的情况是引用两个视图。无法创建不含视图项的两个约束。不含视图想的约束,能够正常编译,不会发出警告,但是,在运行时会抛出异常。