Autolayout 是由一系列线性方程组成,方程格式如下:

item1.attribute [=, >=, <=] item2.attribute * Multiplier + contant

属性

  1. top, bottom, left, right, leading, trailing, centerx, centery
  2. margin 系列(top, bottom, left, right, leading, trailing, centerx, centery
  3. baseline, lastbaseline, firstbaseline
  4. notAnAttribute

优先级

范围:1-1000

  1. 1000 必须满足(required), 如果不满足会 crash
  2. 其他为可选(option)

内在内容大小 (intrinsic content size)

拥有内在内容大小的 view,布局引擎会自动生成约束:

  • labels, buttons, switches, textfields: 定义长度和宽度
  • sliders: 定义长度
  • textview, imageview: 根据内容大小变化
  • 自定义,重写 intrinsicContentSize 方法;

CHCR

  • compression resistance:保证 view 不小于某个足够显示完整内容的值,默认优先级 750
  • content hugging:保证 view 不大于某个值,默认优先级 250

CHCR Tips

  1. 拉伸 View 时,当所有 view 的 content hugging 相同时,布局引擎无法区分需要拉伸哪个 view
    例如:界面有一个 label 和 textfield,希望 label 不动,textfield 拉伸,可以设置 label 的 conent hugging 大于 textfield 的,例如 251,这也是系统的默认行为;

  2. 如果需要保持 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

  1. layoutMarginsGuide
  2. layoutMarginset: UIEdgeInsets,手动设置
  3. preservesSuperviewLayoutMargins,defaut no, 子 view 是否保留父 view 的 margins
  4. subview 与 superview 四周的边距为 20 point, subview 之间为 8 point。
  5. viewcontroller’s root-view: Top bottom:0 left,right:16 or 20 point
  6. layoutMarginsDidChange 边距改变的回调

readableContentGuide

方便大屏阅读(用户不用移动头), 如果内容是文本控件可以用 readableContentGuide 来代替 margins

注意:
在 root view 的 xib 中开启 follow readable width 无效,需要再套一个 view
DEMO:

1
2
3
let guide = view.readableContentGuide
guide.leadingAnchor.constraintEqualToAnchor(textLabel.leadingAnchor).active = true
guide.trailingAnchor.constraintEqualToAnchor(textLabel.trailingAnchor).active = true

Semantic Content

不同语言环境切换时,例如从左到右切换到从右到左,界面元素会跟着反转,为防止某些特殊的控件反转,例如带箭头的 button,可以通过设置semanticContentAttribute 来控制如何反转

  1. unspecified (default, flip)
  2. playback (not flip)
  3. spatial (not flip)
  4. forceLeftToRight
  5. forceRightToLeft
  6. forceLeftToRight

UILayoutGuide

UILayoutGuide 是一个矩形约束,可以用来代替 dammy view,例如:

  1. 当作间隔
  2. 当作容器来包装子 view

使用说明:

  1. 创建 layoutguide
  2. addLayoutGuide: to view
  3. 通过 autolayout 来定义位置和大小

DEMO:间距相同的空格

1
2
3
4
5
6
7
8
9
10
11
let 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

  1. 不要使用 frame, bounds, center
  2. 多使用 stack view
  3. 避免使用固定的长度和宽度,设置固定的 size 丧失了适配的灵活性,可以用最小,最大 size 来做限定
  4. 手动添加约束时,需要设置 translatesAutoresizingMaskIntoConstraints 为 false;

生命周期

Deferred Layout Pass
调用 setNeedLayout 或者 setNeedsUpdateConstraints 来触发,Deferred Layout Pass 可以分解为如下两个 pass:

  1. UpdatePass: updateViewConstraints(viewcontroller) - updateConstraints(view)
  2. LayoutPass: viewWillLayoutSubviews(vc) - layoutSubviews(view)

调试

OC 版本:

1
po [self.view _autolayoutTrace]

swift 版本:

1
po  self.view.valueForKey("_autolayoutTrace")

tableview cell 自适应高度

  1. 开启自适应高度

    1
    2
    tableView.estimatedRowHeight = 85.0
    tableView.rowHeight = UITableViewAutomaticDimension
  2. cell 的内容使用自动布局

iOS 11 新增

Margins
  1. directionalLayoutMargins,符合语言的 marigin,例如从右到左的语言
  2. systemMinimumLayoutMargins UIKit 有一个默认最小的边距,directionalLayoutMargins 有可能小于这个边距,systemMinimum 则选择最小的
  3. viewRespectsSystemMinimumLayoutMargins,defaut yes,控制 systemMinimumLayoutMargins
SafeArea

iOS 11 引入的 UIView 属性,也可以在 iOS 9 使用,
主要属性:

  1. safeAreaLayoutGuide,layout 使用
  2. safeAreaInsets,手动设置使用
  3. additionalSafeAreaInsets 用来给容器的子 view 设置额外的安全区域,例如 navigationbar 的 bar 遮挡区域 或者 给自定义容器设置安全内容区
  4. safeAreaInsetsDidChange
  5. insetsLayoutMarginsFromSafeArea margin 是否和 safearea 同步
UIScrollView

新增 frameLayoutGuide,contentLayoutGuide 来简化 UIScrollView 的布局

frameLayoutguide: Scroll View 的 Frame。
contentLayoutGuide: 决定 Scroll View 的 content size。

官网文档摘记

UIScrollView And Autolayout

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 的影响。

参考

Auto Layout Guide
UIScrollView And Autolayout