朝花夕拾- Autolayout
Autolayout 是由一系列线性方程组成,方程格式如下:
item1.attribute [=, >=, <=] item2.attribute * Multiplier + contant
属性
- top, bottom, left, right, leading, trailing, centerx, centery
- margin 系列(top, bottom, left, right, leading, trailing, centerx, centery
- baseline, lastbaseline, firstbaseline
- notAnAttribute
优先级
范围:1-1000
- 1000 必须满足(required), 如果不满足会 crash
- 其他为可选(option)
内在内容大小 (intrinsic content size)
拥有内在内容大小的 view,布局引擎会自动生成约束:
- labels, buttons, switches, textfields: 定义长度和宽度
- sliders: 定义长度
- textview, imageview: 根据内容大小变化
- 自定义,重写 intrinsicContentSize 方法;
CHCR
- compression resistance:保证 view 不小于某个足够显示完整内容的值,默认优先级 750
- content hugging:保证 view 不大于某个值,默认优先级 250
CHCR Tips
拉伸 View 时,当所有 view 的 content hugging 相同时,布局引擎无法区分需要拉伸哪个 view
例如:界面有一个 label 和 textfield,希望 label 不动,textfield 拉伸,可以设置 label 的 conent hugging 大于 textfield 的,例如 251,这也是系统的默认行为;如果需要保持 intrinsic content size 不变,可以设置为 999 来防止拉伸和压缩。
Fitting size
fitting size 是布局引擎根据约束输出的当前界面展示的尺寸大小;而 intrinsic content size 是输入,autolayout 会根据 intrinsic content size 生成约束;
例如:UIStackView 没有 intristic content size,但可以通过计算 fitting size 来模拟 intristic content size 的特性
注意:设置 stackview 的 CHCR 无效果
Top and bottom layout guides
- iOS 7 透明 bar 引入的特性
- UILayoutsupport protocol height 属性
- Top layout guide:顶部到 bar 底部
- Bottom layout guide: 底部到 bar 的顶部
注:iOS 11 后使用 safearea 代替
layoutMargins
- layoutMarginsGuide
- layoutMarginset: UIEdgeInsets,手动设置
- preservesSuperviewLayoutMargins,defaut no, 子 view 是否保留父 view 的 margins
- subview 与 superview 四周的边距为 20 point, subview 之间为 8 point。
- viewcontroller’s root-view: Top bottom:0 left,right:16 or 20 point
- layoutMarginsDidChange 边距改变的回调
readableContentGuide
方便大屏阅读(用户不用移动头), 如果内容是文本控件可以用 readableContentGuide 来代替 margins
注意:
在 root view 的 xib 中开启 follow readable width 无效,需要再套一个 view
DEMO:1
2
3let guide = view.readableContentGuide
guide.leadingAnchor.constraintEqualToAnchor(textLabel.leadingAnchor).active = true
guide.trailingAnchor.constraintEqualToAnchor(textLabel.trailingAnchor).active = true
Semantic Content
不同语言环境切换时,例如从左到右切换到从右到左,界面元素会跟着反转,为防止某些特殊的控件反转,例如带箭头的 button,可以通过设置semanticContentAttribute 来控制如何反转
- unspecified (default, flip)
- playback (not flip)
- spatial (not flip)
- forceLeftToRight
- forceRightToLeft
- forceLeftToRight
UILayoutGuide
UILayoutGuide 是一个矩形约束,可以用来代替 dammy view,例如:
- 当作间隔
- 当作容器来包装子 view
使用说明:
- 创建 layoutguide
- addLayoutGuide: to view
- 通过 autolayout 来定义位置和大小
DEMO:间距相同的空格1
2
3
4
5
6
7
8
9
10
11let space1 = UILayoutGuide()
view.addLayoutGuide(space1)
let space2 = UILayoutGuide()
view.addLayoutGuide(space2)
space1.widthAnchor.constraintEqualToAnchor(space2.widthAnchor).active = true
saveButton.trailingAnchor.constraintEqualToAnchor(space1.leadingAnchor).active = true
cancelButton.leadingAnchor.constraintEqualToAnchor(space1.trailingAnchor).active = true
cancelButton.trailingAnchor.constraintEqualToAnchor(space2.leadingAnchor).active = true
clearButton.leadingAnchor.constraintEqualToAnchor(space2.trailingAnchor).active = true
Rules of Thumb
- 不要使用 frame, bounds, center
- 多使用 stack view
- 避免使用固定的长度和宽度,设置固定的 size 丧失了适配的灵活性,可以用最小,最大 size 来做限定
- 手动添加约束时,需要设置 translatesAutoresizingMaskIntoConstraints 为 false;
生命周期
Deferred Layout Pass
调用 setNeedLayout 或者 setNeedsUpdateConstraints 来触发,Deferred Layout Pass 可以分解为如下两个 pass:
- UpdatePass: updateViewConstraints(viewcontroller) - updateConstraints(view)
- LayoutPass: viewWillLayoutSubviews(vc) - layoutSubviews(view)
调试
OC 版本:1
po [self.view _autolayoutTrace]
swift 版本:1
po self.view.valueForKey("_autolayoutTrace")
tableview cell 自适应高度
开启自适应高度
1
2tableView.estimatedRowHeight = 85.0
tableView.rowHeight = UITableViewAutomaticDimensioncell 的内容使用自动布局
iOS 11 新增
Margins
- directionalLayoutMargins,符合语言的 marigin,例如从右到左的语言
- systemMinimumLayoutMargins UIKit 有一个默认最小的边距,directionalLayoutMargins 有可能小于这个边距,systemMinimum 则选择最小的
- viewRespectsSystemMinimumLayoutMargins,defaut yes,控制 systemMinimumLayoutMargins
SafeArea
iOS 11 引入的 UIView 属性,也可以在 iOS 9 使用,
主要属性:
- safeAreaLayoutGuide,layout 使用
- safeAreaInsets,手动设置使用
- additionalSafeAreaInsets 用来给容器的子 view 设置额外的安全区域,例如 navigationbar 的 bar 遮挡区域 或者 给自定义容器设置安全内容区
- safeAreaInsetsDidChange
- insetsLayoutMarginsFromSafeArea margin 是否和 safearea 同步
UIScrollView
新增 frameLayoutGuide,contentLayoutGuide 来简化 UIScrollView 的布局
frameLayoutguide: Scroll View 的 Frame。
contentLayoutGuide: 决定 Scroll View 的 content size。
官网文档摘记
In general, Auto Layout considers the top, left, bottom, and right edges of a view to be the visible edges. That is, if you pin a view to the left edge of its superview, you’re really pinning it to the minimum x-value of the superview’s bounds. Changing the bounds origin of the superview does not change the position of the view.
subview 的合成的位置受 superview‘s bounds 的 origin 影响,公式为:
x = subview.origin.x - superview.bounds.origin.x
y = subview.origin.y - superview.bounds.origin.y
而 Autolayout 约束的就是合成后的位置,所以不受 bounds origin 的影响。