视图项#######
约束用firstItem和secondItem属性引用它所影响的视图。这些属性是只读的,而且只能在约束创建期间设置。
这两个属性的类型为id类型。
约束,层次结构和边界系统######
当约束引用两个视图时,这两个视图一定要属于同一个视图层次结构。对于引用两个视图的约束,只有两种合法的情况:要么一个视图是另一个视图的父视图(即firstItem是secondItem的祖先,或者反过来),要么两个视图必须是某种类型的兄弟。如果视图让约束引用其他情况的视图,将会崩溃的一塌糊涂。
使用约束的时候,经常会出现这些检查,NSView提供了ancestorSharedWithView:方法,但是UIView没有提供。
安装约束#######
要让约束进入Auto Layout系统,需要将约束添加到视图中。举例如下:
[myView addConstraint:aConstraintInstance];
因为可视化个事系统返回约束的数组,而不是返回单个约束,所以NSLayoutConstraint类还提供了一种同步添加约束集合的方式。
[myView addConstraints:myArrayOfConstraints];
约束在他们的firstItem和secondItem属性中的最近公共祖先中总有一个天然归宿,
view1和view3之间的约束,应该添加到作为父视图的view3中。
同样,view2和view3之间的约束也属于view3.虽然view3不是view2的父视图,但view3是view2的祖先。
对于view1和view2来说,view3是他们的最近的公共祖先。尽管没有提到是作为第一项还是第二项,但是应该将view1和view2之间的约束添加到view3中。
IB遵循这一规则,我们也应该遵循这一规则,如果要了解每个约束位于IB层次结构中的什么位置,可以在添加到第一项和第二项的最近的公共祖先中的故事版大纲内找到。
约束通常安装在该约束所引用视图的最近的公共祖先中。约束有必要安装在引用的每个视图的公共祖先中。约束中的数字相对于所安装视图的坐标系有意义。视图被视为自身的祖先。
事实上,每个约束自身可以而且应该安装到一个自然且正确的目标视图中。约束在两个视图之间寻找最近的公共祖先,并且将并且将自身,添加到该视图上。如果是一元约束,则安装到第一个视图上。
使用这个类别,可以将针对约束本身调用的install(remove)方法。你需要的关于安装位置的信息已经存在于每个(合法定义)约束中。此外,如果你的约束统一安装在一致的目标中,则删除约束也会很轻松。
autoLayout还提供了一种删除约束的笨办法,从层次结构中删除视图时,自动删除约束。
[view removeFromSuperview];
然后,引用该视图的约束都会从Auto Layout中自动删除。例如,假设有一个约束,他描述了view1 和 view2之间的关系。view3拥有该约束,因为它是view1 和view2的最近公共祖先。当将view2从其父视图上移除的时候,autoLayout会自动删除view3中的约束。这种删除是系统自动为你删除的,不需要显示的请求。
删除约束######
任何时候,都可以在视图中添加和删除约束。两个内置方法removeConstraint:和removeConstraints:可用来删除给定视图中的一个约束或者一个约束数组。因为这些发放注定是针对目标指针的。因此当视图删除约束时,他们可能不会如你所愿。
例如,假设你构建了一个中心匹配约束,并将该约束添加到了你的视图中。那么你不能用同样的约束去构建该约束的另一个版本。并预期调用removeConstraint方法删除该约束。虽然这两个约束是等价的,但他们不是同一个约束。
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:textField attributeLNSLayoutAttributeCenterX releatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f]];
[self.view removeConstraint:[NSLayoutConstraint constraintWithItem:textField attributeLNSLayoutAttributeCenterX releatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f]];
调用这两个方法后,最终得到的结果:self.view实例中仍然包含原来的约束,试图删除第二个约束的请求被忽略了。未能成功删除该视图不支持的约束。
要解决这个问题,有两个办法。第一个办法是在第一次添加约束时候,将他存储在一个局部的变量中,以保存该约束。
代码如下:
NSLayoutConstraint *myConstraint =[ NSLayoutConstraint constraintWithItem:textField attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRElationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f]];
[self.view addConstraint:myConstraint];
[self.view removeConstraint:myConstraint];
要删除一个等价的约束,可以使用上面这样的办法,也可以使用这样的办法。将两个约束进行比较,并删除在数学上与你传递的约束相匹配的约束。第二个办法是用类类别给约束做标记,以便将来找到并删除该约束。删除约束是autolayout 中的一个重要的功能。每当刷新布局时(在设备旋转时最长发生这种情况),都需要删除无效的约束,并用新的约束规则替他们。
知道自己的约束是静态的(在视图的整个生命周期都有效)还是动态的(根据需要更新)可帮助你决定需要何种方法。如果你觉得以后可能需要删除一个约束,可以通过局部变量保存该约束,以便以后从视图中删除;也可以采用以后能搜索该约束的变通方法。
比较约束######
所有约束都使用下列形式的固定结构以及一个关联的优先级。
视图1.属性 R 视图2.属性 * 乘数 + 常数;
上述公式中的每个元素通过约束的对象属性提供。即priority,firstItem,firstAttribute,relation,secondItem,multiplier和constant。使用这些属性可以很方便的将两个约束进行比较。
视图将约束最为对象来存储和删除。如果两个约束存储在内存中的不同位置,那么即使这两个约束描述了相同的条件,系统也会认为是两个不同的约束。要使代码不存储在局部变量中,直接添加和删除约束,可以使用比较功能来实现。
下面代码中创建了一个NSLayoutConstraint类别,用来比较两个约束直接的属性,并判断他们是否匹配。isEqualToLayoutConstraint:方法用来判断是否等价,而不考虑优先级。不管开发人员当前指定的优先级如何,两个描述相同条件的约束,在本质上都是等价的。
- (BOOL) isEqualToLayoutConstraint : (NSLayoutConstraint *) constraint{
if (self.firstItem != constriant.firstItem) return NO;
if (self.secondItem != constriant.secondItem) return NO;
if (self.firstAttribute != constriant.firstAttribute) return NO;
if (self.secontAttribute != constriant.secontAttribute) return NO;
if (self.relation != constriant.relation) return NO;
if (self.multiplier != constriant.multiplier) return NO;
if (self.constant != constriant.constant) return NO;
return YES:
}
匹配约束######
安装了约束后,可以对该约束做两件事,可以删除该约束(也许是用一个新规则替换它),也可以修改约束的常数(通常是为视图添加动画效果)。Auto Layout经常使用这两个任务来创建响应用户交互请求的生动界面。
尽管可以创建指向特定用户约束的实例变量和输出口,但是具有高度交互能力的GUI可以创建大量即使添加,修改和删除的约束。轻量级和生命周期较短的约束由于存在时间不长,因此可能不值得直接指向他们。
布局约束法则######
下面是一些布局约束的基本法则,应答牢记:
1.布局约束是有优先级的 优先级的范围在1到1000之间。优先级高的约束总是比优先级低的约束先得到满足。可以指定的最高优先级是“必须的”优先级(值为1000)。它也是默认优先级。在布局期间,系统浏览你添加的所有约束,并试图全部满足他们。在决定哪个约束的影响较小时,需要用到优先级。当两个约束有冲突的时候,优先级为99的约束会让步优先级为100的约束。
2.布局约束没有任何超越优先级的天然“顺序” 所有具有相同优先级的约束都被同时考虑。如果需要优先处理某个约束,就要赋予该约束更高的优先级。
3.布局约束是关系,没有方向。不必通过解出右端来计算左端。
4.布局约束可以取近似值 可选的约束试图优化他们的结果。假如有这样的约束 view2的顶边应该与view1的底边位于相同的位置。那么约束系统会尽量使这两个视图之间的距离最小,从而将他们挤压到一起。如果有其他防止这两个视图相互接触的约束,那么系统会把他们放的尽可能靠近,最小化这两个视图之间的绝对距离。
5.布局约束可以循环 只要所有条件都满足,哪个元素引用哪个元素无关紧要。不要忌讳交叉引用。在这种声明式的系统中,可以进行循环引用,不会遇到无限循环的问题。
6.布局约束可以冗余 如果约束不户型冲突,则可以放心地安装多个实现统一布局的约束。
7.布局约束可以引用兄弟视图 只要两个视图有共同的视图祖先,既可以将一个视图的子视图中心点与另一个完全不同的视图的中心点对齐。例如,可以创建一个复杂的文本输入视图,并将其最右边的属性与它下方的嵌入式图像视图的右边属性对齐。也可以两个视图一起移动,但是哪一个视图都不是另一个视图的父视图。
8.Auto Layout对变形的处理可能不是很好 将autoLayout与变形混用时,要特别小心,尤其是与包括旋转的变形混用的时候。
Auto Layout仅支持保护矩形的变形
在带有不保护矩形的边界变形图的视图上,auto layout不支持非0得对齐insert.
9.布局约束不应该在不同的边界系统之间交叉使用。不要交叉进出滚动视图,集合视图和标示图来进行对齐。如果有某种自带边界系统的内容视图。不要跳出另一个视图所在的完全不同的边界系统中。这么做也许不会与你的应用相冲突,但是最好不要这样做,而且auto layout对它的支持性也不是很好。
auto Layout 仅支持保护矩形的交叉边界变形。
auto Layout 不支持交叉使用旋转边界变形与边缘布局约束,如右边,左边,顶部,底部的约束。
auto Layout 不支持交叉使用旋转边界变形与尺寸布局约束,如宽度和高度约束。
10.布局约束会在运行时失败 如果你的约束不能被解析并且与其他约束有冲突。那么系统会在运行时,放弃那个约束,以便将视图呈现出来。不过这样呈现的布局通常比较难看,很难反应你的意图。 Auto Layout会将所有关于错误的描述发送给Xcode控制台。这些报告可以用来修复约束,使个约束之间彼此协调。
11.格式不正确的布局约束会中断应用程序的执行 尽管我们还没有详细的介绍可视化个事。 但是仍然要注意一些可能通过未处理异常导致应用程序崩溃的约束调用。
12.约束至少引用一个视图。
13.小心无效的属相结对 将一个视图的左边与另一个视图的高度相配是无效的。无效的属性结对会在运行时抛出异常。特别要指出的是:不要将尺寸属性与边缘属性混合使用。
14.合理使用Auto Layout.