深入剖析AutoLayout 之 尺寸计算
02 Mar 2015首先回顾一下Auto Layout诞生前, 计算视图所需空间的方法:
-sizeThatFits:
以当前frame.size为参数, 计算视图所需空间, 默认返回CGSizeZero, 常用于UILabel、UIButton等的自适应.
-sizeToFit
默认实现将-sizeThatFits:的返回值设为占用空间, 但需说明的是, -sizeToFit不改变其origin, 只改变bounds和center.
这里的问题在于:
1. 多数视图并不能很好地支持-sizeThatFits:/-sizeToFit, 如UIView只有默认实现, UILabel不支持多行;
2. 视图结构树上的每一层都需要支持-sizeThatFits:/-sizeToFit, 才可以在顶层使用-sizeThatFits:/-sizeToFit;
3. 假使子视图都支持-sizeThatFits:/-sizeToFit, 父视图在实现-sizeThatFits:和-layoutSubviews时也不得不保留两套逻辑相近的代码, 违背单一修改原则;
4. 没有对hug/compress情况做出支持, 任何相关逻辑都需要override -layoutSubviews;
5. 即使在retina屏下, -sizeThatFits:/-sizeToFit也不支持小数(.5f).
引入Auto Layout的同时引入了两个计算视图所需空间的方法:
相当于调用
其中的targetSize指视图的可用空间, 而UILayoutPriorityFittingSizeLevel是一个相当低的优先级(50).
由于-systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:仅依赖约束、intrinsic content size和priority, 基本不需要override -systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:.
这里举个例子说明-systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:中的优先级.
假设button的intrinsic content size为{50, 50}; horizontal/vertical compression resistance priority为UILayoutPriorityDefaultHigh;
调用[button systemLayoutSizeFittingSize:(CGSize){10, 10} withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityDefaultLow]的结果为{10, 50}.
换言之, 作为参数的优先级是指可用空间的约束力, 在可用空间优先级低于compression resistance priority时, 返回结果就可能大于布局空间.
Auto Layout预设了两种尺寸:
在iPhone Simulator上, UILayoutFittingCompressedSize = {0, 0}, UILayoutFittingExpandedSize = {10000, 10000}, 与名字一致, 分别对应极小空间和极大空间. 适当取用这两个值, 可以提高代码的可读性.
补充说明几点:
-
-sizeThatFits:/-sizeToFit完全独立于Auto Layout运作, 设置宽高constraint完全不影响-sizeToFit;
-
-systemLayoutSizeFittingSize:会触发-updateConstraintsIfNeeded已计算最新intrinsic content size;
-
-systemLayoutSizeFittingSize:可能返回小数值(.5f).
-systemLayoutSizeFittingSize:的适用面极广, 举例说, iOS8起, 可以在计算table view cell height时返回UITableViewAutomaticDimension, 让Auto Layout自动决定cell的高度, 这里依赖的机制就是-systemLayoutSizeFittingSize:. 在iOS8之前, 一样可以通过-systemLayoutSizeFittingSize:简化高度计算, 示例代码如下:
妥善适用-systemLayoutSizeFittingSize:, 避免在代码中使用magic number.
-EOF-