AutoLayout中可能出现三类异常,Unsatisfiable Layouts, Ambiguous Layouts 和 Logical Errors。
Unsatisfiable Layouts
设计时,当系统无法满足所有Constraints时,一般是因为出冲突,这是可以看到在冲突的View Controller上面看到红色的按钮,点进去可以看到冲突的Constraints。
有些冲突是在运行时才导致的,例如横竖屏切换,此时,AutoLayout开始逐一破坏这些冲突的Constraints,直到计算成功为止,这些冲突以及被破坏的Constraints会被打印到控制台。
常见的导致Unsatisfiable Layouts的场景:
- 使用代码直接添加View到View结构树种,在IB中,IB会自动将View的translatesAutoresizingMaskIntoConstraints属性置为NO,而在代码中新建的View默认为YES,需要在添加约束前手动改为NO;
- 当空间特别小时,也容易导致Unsatisfiable Layouts,为了尽可能在出现这种情况时,尽可能的保护布局,可以考虑将一些Constraints的优先级从Required(1000)降到High(750),这样,当出现冲突时,这些优先级较低的Constraints会更先被破坏,尽可能保证原有的布局。
Ambiguous Layouts
出现Ambiguous Layout一般是两种情况:
- 需要更多的Constraints来计算布局;
- 当有多个同优先级的备选Constraints冲突时,系统不知道应该破坏哪一个。
出现Ambiguous Layout时,如果在设计阶段,是比较容易解决的,如果在运行时,Auto Layout会选择其中一种计算结果作为布局,此时布局很可能是不可控的,并且,被破坏的Constraints并不会打印到控制台。
通过下述的一些方法和属性,可以辅助调试Ambiguous Layouts,首先,设置一个断点:
- hasAmbiguousLayout属性:该属性标示对应的View是否有Ambiguous Layout;
- exerciseAmbiguityInLayout方法:当确定一个View有Ambiguous Layout时,在该View上调用该方法,可以让系统在多个可能的方案中切换;
- constraintsAffectingLayoutForAxis方法:在View上调用该方法,返回一组在对应的轴上的所有影响的Constraints;
- constraintsAffectingLayoutForOrientation方法:在View上调用该方法,返回一组在对应的横竖屏朝向上的所有影响的Constraints;
- _autolayoutTrace方法:私有方法,在View上调用该方法,可以打印出包含该View的View结构树的信息,Ambiguous Layout的Views以及translatesAutoresizingMaskIntoConstraints为YES的Views都会被打印出来。
Logical Errors
Logical Errors指的是布局有时候虽然没有明显的错误,但是得到的结果与预想的不符,这个没有定位方法,只能通过检查和调试来解决。
Debugging Tricks and Tips
一些Debug技巧:
Logs
控制台打印出来的Log,例如:
1 | 2015-08-26 14:27:54.790 Auto Layout Cookbook[10040:1906606] Unable to simultaneously satisfy constraints. |
可以明显看出,第一个和第五个Constraints冲突,系统破坏了第一个。
如果View的translatesAutoresizingMaskIntoConstraints为YES时,会有一些额外的信息,例如:
1 | <NSAutoresizingMaskLayoutConstraint:0x7ff28252e480 h=--& v=--& H:[UIView:0x7ff282617cc0(50)]>" |
其中,h=后面带着三个参数,代表左边界,宽度和右边界,v=后面带着三个参数,上边界,高度和下边界,“-”代表固定值,“&”代表可变值。
Adding Identifiers
为了更好在Log中识别信息,可以给View设置identifier属性,如图:
当没有设置时,XCode也会用一些属性进行标示,例如Label的Text,Button的Title,Text Filed的Placeholder。
Visualizing Views and Constraints
在虚拟器上运行App,然后在XCode中选择:
1 | Debug > View Debugging > Show Alignment Rectangles |
可以看到控件的矩形,如下图:
选择:
1 | Debug > View Debugging > Capture View Hierarchy |
可以看到整个View的结构,如下图:
Edge Case
下面是一些可能出现的Edge Case:
- Auto Layout是根据View的Alignment矩形来布局位置的,而不是Frame,绝大多数情况下,这两个值都是相同的,但是有些情况下,经过计算后,View的Alignment矩形可能会没有覆盖View的全部;
- 利用View的Transform属性来旋转,缩放和移动时,AutoLayout不会重新进行计算;
- 有些View的内容可能超过其bounds;
- NSLayoutAttributeBaseline, NSLayoutAttributeFirstBaseline,和NSLayoutAttributeLastBaseline三个参数只有当其内部高度足够时,才会显示正常,否则可能会异常;
- Constraints的优先级是全局的,这意味着即使是在StackView中View的Constraints,也可能会与StackView外View的Constraints根据优先级进行计算;
- Aspect ratio允许垂直和水平的Constraint进行交互,这将导致两个方向的Constraints互相产生影响,可能导致冲突或者异常布局。