一,参考翻译范文:
https://juejin.im/post/5a951c655188257a804abf94
二,自动布局的三个步骤
第一步是更新约束
第二步是重新布局
第三步是重绘显示
三,布局
一个视图的布局指的是它在屏幕上的的大小和位置。每个 view 都有一个 frame 属性,用来表示在父 view 坐标系中的位置和具体的大小。UIView 给你提供了用来通知系统某个 view 布局发生变化的方法,也提供了在 view 布局重新计算后调用的可重写的方法
- UIview的方法layoutSubviews
- 这个 UIView 方法处理对视图(view)及其所有子视图(subview)的重新定位和大小调整。它负责给出当前 view 和每个子 view 的位置和大小。这个方法很开销很大,因为它会在每个子视图上起作用并且调用它们相应的 layoutSubviews 方法
- 不应该显示调用,应该触发使用,那样消耗更低
- UIViewController生命周期的layoutSubviews
- 当 layoutSubviews 完成后,在 view 的所有者 view controller 上,会触发 viewDidLayoutSubviews 调用。因为 viewDidLayoutSubviews 是 view 布局更新后会被唯一可靠调用的方法,所以你应该把所有依赖于布局或者大小的代码放在 viewDidLayoutSubviews 中,而不是放在 viewDidLoad 或者 viewDidAppear 中
- 自动刷新触发器
- 修改 view 的大小
- 新增 subview
- 用户在 UIScrollView 上滚动(layoutSubviews 会在 UIScrollView 和它的父 view 上被调用)
- 用户旋转设备
- 更新视图的 constraints
- 主动刷新触发器
- setNeedsLayout
- setNeedsLayout 方法会立刻执行并返回,但在返回前不会真正更新视图。视图会在下一个 update cycle 中更新,就在系统调用视图们的 layoutSubviews 以及他们的所有子视图的 layoutSubviews 方法的时候。即使从 setNeedsLayout 返回后到视图被重新绘制并布局之间有一段任意时间的间隔,但是这个延迟不会对用户造成影响,因为永远不会长到对界面造成卡顿
- layoutIfNeeded()
- 与 setNeedsLayout() 会让视图在下一周期调用 layoutSubviews 更新视图不同,layoutIfNeeded 会立即调用 layoutSubviews 方法。但是如果你调用了 layoutIfNeeded 之后,并且没有任何操作向系统表明需要刷新视图,那么就不会调用 layoutsubview。如果你在同一个 run loop 内调用两次 layoutIfNeeded,并且两次之间没有更新视图,第二个调用同样不会触发 layoutSubviews 方法
- 当对希望通过修改 constraint 进行动画时,这个方法特别有用。你需要在 animation block 之前对 self.view 调用 layoutIfNeeded,以确保在动画开始之前传播所有的布局更新。在 animation block 中设置新 constrait 后,需要再次调用 layoutIfNeeded 来动画到新的状态
-
- setNeedsLayout
四,显示
一个视图的显示包含了颜色、文本、图片和 Core Graphics 绘制等视图属性,不包括其本身和子视图的大小和位置。和布局的方法类似,显示也有触发更新的方法,它们由系统在检测到更新时被自动调用,或者我们可以手动调用直接刷新
- 自动刷新显示draw rect方法
- 对视图内容显示的操作,类似于视图布局的 layoutSubviews ,但是不同于 layoutSubviews,draw 方法不会触发后续对视图的子视图方法的调用。同样,和 layoutSubviews 一样,你不应该直接调用 draw 方法,而应该通过调用触发方法,让系统在 run loop 中的不同结点自动调用
- 主动刷新显示setNeedsDisplay()方法
- 这个方法类似于布局中的 setNeedsLayout 。它会给有内容更新的视图设置一个内部的标记,但在视图重绘之前就会返回。然后在下一个 update cycle 中,系统会遍历所有已标标记的视图,并调用它们的 draw 方法。如果你只想在下次更新时重绘部分视图,你可以调用 setNeedsDisplay(_:),并把需要重绘的矩形部分传进去(setNeedsDisplayInRect in OC)。大部分时候,在视图中更新任何 UI 组件都会把相应的视图标记为“dirty”,通过设置视图“内部更新标记”,在下一次 update cycle 中就会重绘,而不需要显式的 setNeedsDisplay 调用。然而如果你有一个属性没有绑定到 UI 组件,但需要在每次更新时重绘视图,你可以定义他的 didSet 属性,并且调用 setNeedsDisplay 来触发视图合适的更新。
- 视图的显示方法里没有类似布局中的 layoutIfNeeded 这样可以触发立即更新的方法。通常情况下等到下一个更新周期再重新绘制视图也无所谓
-
-
五,约束
- updateConstraints()
- 和布局中的 layoutSubviews() 方法或者显示中的 draw 方法类似,updateConstraints() 只应该被重载,绝不要在代码中显式地调用。通常你只应该在 updateConstraints 方法中实现必须要更新的约束
- setNeedsUpdateConstraints()
- 调用 setNeedsUpdateConstraints() 会保证在下一次更新周期中更新约束。它通过标记“update constraints”来触发 updateConstraints()。这个方法和 setNeedsDisplay() 以及 setNeedsLayout() 方法的工作机制类似
- updateConstraintsIfNeeded()
- 对于使用自动布局的视图来说,这个方法与 layoutIfNeeded 等价。它会检查 “update constraints”标记(可以被 setNeedsUpdateConstraints 或者 invalidateInstrinsicContentSize方法自动设置)。如果它认为这些约束需要被更新,它会立即触发 updateConstraints() ,而不会等到 run loop 的末尾
- invalidateIntrinsicContentSize()
- 自动布局中某些视图拥有 intrinsicContentSize 属性,这是视图根据它的内容得到的自然尺寸。一个视图的 intrinsicContentSize 通常由所包含的元素的约束决定,但也可以通过重载提供自定义行为。调用 invalidateIntrinsicContentSize() 会设置一个标记表示这个视图的 intrinsicContentSize 已经过期,需要在下一个布局阶段重新计算
-
- 自动布局中某些视图拥有 intrinsicContentSize 属性,这是视图根据它的内容得到的自然尺寸。一个视图的 intrinsicContentSize 通常由所包含的元素的约束决定,但也可以通过重载提供自定义行为。调用 invalidateIntrinsicContentSize() 会设置一个标记表示这个视图的 intrinsicContentSize 已经过期,需要在下一个布局阶段重新计算
六,约束,布局,显示的对比图
七, update cycle 和 event loop 之间的交互
八,动态布局需要考虑的要素
参考:http://www.10tiao.com/html/330/201711/2653579435/2.html
1,运行时变换布局
2,跟踪触摸手势
3,动态字体
4,安全区
5,比例布局
6,stackView
九,苹果iOS UI 布局有以下三种主要方法:
Frame layout:直接用代码设置每个控件的 Frame。
使用 auto-resizing masks:可以自动调整子视图与父视图之间的位置关系。
使用 auto-layout。
使用 frame layout 最直接,最灵活。当需要发生变化的时候,所有的改变都有你自己来控制。它带来的问题就是过于繁杂。即使一个很简单的界面,为了适配各种可能的情况,都要花费大量的精力去设计、调试、维持。Frame 的性能高。
使用 auto-resizing masks 可以部分地解决上述问题。但它只是完整的解决方案中的一个子集,无法适应所有可能的情况。而且它只能应对来自外部的改变,如 macOS 中用户调整了窗口大小、iOS中的横屏和分屏。而内部的改变如本地化、app 展示的内容改变这类的问题无法解决。
使用 auto-layout 可以动态地解决内部改变和外部改变。它不直接设置控件的大小和位置,而是通过约束去考虑控件之间的相对关系。功能强大,但可能有点抽象,跟传统的方法不太一致。 AutoLayout 性能低,而且代码冗长蹩脚
十,布局的选择
1,demo级
frame,autoLayout
2,局部级
stactView,UICollectionView
3,页面级
Masonry(基于AutoLayout),MyLayout(基于Frame)
4,性能级
ComponentKit和AsyncDisplayKit(faceBook)
5,跨平台级
flexBox和Yoga