AutoLayout进阶
零、参考文档
一、布局过程
在AutoLayout下,视图的布局过程:
updating constraints:
这个过程是我们根据一些条件更新、新增、删除视图的约束的地方。这个过程之后所有的视图约束就都创建好了,后续会根据现有的约束计算布局。update layout:
这个过程真正计算布局的地方,系统根据约束计算好之后我们还可以在layoutSubviews
内做一些自定义的布局。重要事项
setNeedsUpdateConstraints:
是标记视图为需要更新约束,保证调用updateConstraintsIfNeeded
的时候系统调用updateConstraints
,或者系统在下一个运行循环(Runloop)的时候调用updateConstraints
。setNeedsLayout:
是标记视图为需要重新布局,保证调用setNeedLayout
的时候系统调用layoutSubviews
,或者系统在下一个运行循环管(Runloop)的时候调用layoutSubviews
。- 任何对视图约束的操作(更新、新增、删除)之后都会自动调用
setNeedLayout
,就会在下一个运行循环(Runloop)或者我们自己调用layoutIfNeed
的时候调用layoutSubviews
。
二、固有内容尺寸(Intrinsic Content Size )
固有内容尺寸是一个视图期望为其显示特定内容得到的大小。UILabel
会根据字体和内容自己计算一个固有大小,多行文本的时候可以设置preferredMaxLayoutWidth
来计算多行文本的固有大小。UIView
没有固有大小,但是我们可以重写intrinsicContentSize
,根据视图的内容自己计算视图的固有大小。
三、压缩阻力(Compression Resistance) 吸附阻力( Content Hugging)
contentCompression:
阻止自己被压缩的优先级,如果两个两个视图出现相互挤压的时候,优先级高的不会被压缩,优先级底的被压缩。例如:当两个UILabel
水平相互压缩的时候,我们可以指定不想压缩的UILabel
的阻止压缩的优先级高一点。contentHugging:
阻止自己被吸附的优先级,当一个视图的Frame
改变的时候,可以导致另外一个依赖此视图的视图布局发生改变,设置阻止吸附优先级高一点,可以阻止这个改变。优先级(我们可以自己设定值 eg:500)
- UILayoutPriorityRequired = 1000
- UILayoutPriorityDefaultHigh = 750
- UILayoutPriorityDefaultLow = 250
- UILayoutPriorityFittingSizeLevel = 50
四、Alignment Rect
AutoLayout
中的约束都是基于Alignment Rect
进行后面的布局计算的。一般情况下我们不做任何处理Alignment Rect
是和Frame
一样的。Alignment Rect
是要我们指定视图的要基于的核心元素的大小。
比如,一个自定义 icon 类型的按钮比我们期望点击目标还要小的时候,这将会很难布局。当插图显示在一个更大的 frame 中时,我们将不得不了解它显示的大小,并且相应调整按钮的 frame,这样 icon 才会和其他界面元素排列好。当我们想要在内容的周围绘制像 badges、阴影、倒影的装饰时,也会发生同样的情况。通过重写以下方法可以指定视图的Alignment Rect
。
- alignmentRectInsets:
- alignmentRectForFrame:
- frameForAlignmentRect:(
要和alignmentRectForFrame可逆
)
五、AutoLayout动画
只有在动画过程中触发视图的重新布局layoutSubviews
才会有动画效果。一般我们先修改视图的约束,系统会自动调用setNeedLayout
,然后我们要做的就是在动画过程中调用layoutIfNeed
触发系统调用layoutSubviews
.
1 2 3 4 |
|
六、AutoLayout调试
1. 不可满足的约束条件
当因为有不满足约束条件而抛出异常的时候,我们可以打上断点,如果不是很明确是哪个视图导致的问题,你就需要通过内存地址来辨认视图:
1 2 3 4 |
|
一个更直观的方法是在控制台修改有问题的视图,这样你可以在屏幕上标注出来。比如,你可以改变它的背景颜色:
1
|
|
然后从断点处继续执行,就可以看到效果。 你也可以通过改进错误信息本身,来更容易地在 iOS 中弄懂不可满足的约束条件错误到底在哪里。我们可以在一个 category 中重写 NSLayoutConstraint 的描述,并且将视图的 tags 包含进去:
1 2 3 4 5 6 7 8 9 10 11 |
|
如果整数的 tag 属性信息不够的话,我们还可以得到更多新奇的东西,并且在视图类中增加我们自己命名的属性,然后可以打印到错误消息中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
另外还有一个打印调试信息的方法: issue-3-auto-layout-debugging
2. 有歧义的布局
UIView提供了三种方式来查明有歧义的布局:hasAmbiguousLayout
,exerciseAmbiguityInLayout
,和私有方法 _autolayoutTrace
。如果我们不想自己遍历视图层并记录这个值,可以使用私有方法 _autolayoutTrace。这将返回一个描述整个视图树的字符串:类似于 recursiveDescription 的输出(当视图存在有歧义的布局时,这个方法会告诉你)。
1 2 3 4 5 6 7 |
|
另一个标识出有歧义布局更直观的方法就是使用 exerciseAmbiguityInLayout。这将会在有效值之间随机改变视图的 frame。然而,每次调用这个方法只会改变 frame 一次。所以当你启动程序的时候,你根本不会看到改变。创建一个遍历所有视图层级的辅助方法是一个不错的主意,并且让所有包含歧义布局的视图晃动
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
3. NSUserDefault选项
有几个有用的 NSUserDefault 选项可以帮助我们调试、测试自动布局:
– UIViewShowAlignmentRects
: 设置视图可见的alignment rects
。
– NSDoubleLocalizedStrings
: 简单的获取并复制每个本地化的字符串。这是一个测试更长语言布局的好方法。
– AppleTextDirection
和 NSForceRightToLeftWritingDirection
: 模拟从右到左的语言
我们可以通过代码
和scheme editor
设置。
1
|
|
七、AutoLayout性能
自动布局是布局过程中额外的一个步骤。它需要一组约束条件,并把这些约束条件转换成 frame。因此这自然会产生一些性能的影响。如果我们对一些复杂的视图的性能要求比较高,就避免通过约束来布局,直接通过计算视图的frame来布局。
八、总结
自动布局是一个帮助我们灵活布局的工具,我们熟悉了以上这些自动布局的知识后,在UI布局层面会给我们很多乐趣。