[Android开发学iOS系列] Auto Layout

内容:

  • 介绍什么是Auto Layout.
  • 根本运用办法
    • 在代码中写束缚的办法
  • Auto Layout的原理
  • 尺度和优先级
  • Auto Layout的运用细则
    • 重要的特点
    • StackView
    • Layout Guide
  • Performance
  • Debugging

What is Auto Layout

Auto Layout会依据constraints(束缚)动态核算出view hierarchy中所有View的方位和巨细.

关于Android开发者来说, Auto Layout很容易上手, 它十分像ConstraintLayoutRelativeLayout: 给View规则它上下左右和谁对齐, 决议UI的方位和巨细.

Auto Layout的束缚更广泛一些, 不仅仅是两个View之间的联系, 还有宽高, 比率等设置, 而且能够有一些大于小于等的规模设置.

Auto Layout不是一个View

开端学Auto Layout我还以为它是一个叫AutoLayout的View, 把其他子View包进去然后设置一些放置规矩, 就类似于Android的ConstraintLayout或许RelativeLayout.

可是其实不是, AutoLayout不是一个具体的View, 它代表的是一种核算引擎. 因为在代码里你从来不需求写AutoLayout这个关键字, 写的从来都是Constraints.

开发者为View设置足够多的束缚, 规则和这个View方位和巨细相关的因素, 这个引擎就能够为咱们核算出View的方位和巨细.

AutoLayout为了解决什么问题

不同屏幕适配; 能够合理应对改动的responsive UI.

改动布局有内外两种因素, 除了屏幕尺度, 屏幕旋转, 窗口巨细改动等外部因素.

内部因素还包含了内容的动态改动, 国际化的支撑, 字体的调整等.

和Auto Layout平行的解决方案是什么

摆放UI有三种主要的办法:

  • 在程序里给每个View设置frame.
  • 设置frame, 结合运用autoresizing masks来应对外部改动. (autoresizing mask界说了一个view的frame在它的superview frame改动时应该怎样改动.)
  • 运用Auto Layout.

能够看出第二种只是在根据frame的办法上做出了一点改善, 所能应对的也仅仅是外部改动, 有必定的局限性. 所以能够把前两种归类为一种.

这也正是Auto Layout呈现之前的解决方案, 即根据frame的布局办法.

Auto Layout的思考点不再着眼于view frame, 而是view的relationship.

怎样运用Auto Layout

写iOS的UI有多种办法, Auto Layout归于UIKit, 在写的时分, 能够用storyboard, 也能够直接在代码中写束缚.

在storyboard里边有一些好处, 比方所见即所得, 而且ide会给出一些warnings, 比方控件在storyboard上的方位与束缚不一致, 会提示, 而且能够挑选办法修正. 在storyboard里边写束缚的确是不容易犯错的一种办法, xcode的操作也很直观, 这儿不做演示了.

之前咱们也讨论过, 用storyboard写UI存在阅览性差, 代码版本办理和团队合作都有问题等. 所以具体运用需求看实际情况.

关于束缚, location和size的束缚不能混着用, 这个也是从逻辑上就能够了解的. 比方让某个view的top和parent的top对齐(或许再offset个常量)是能够的, 可是让top等于某个size就不能了解了.

在代码中创建束缚

假如不必Interface Builder, 而是挑选在代码中创建束缚, 那么仍然有多种挑选:

  • 运用layout anchor.
  • 运用NSLayoutConstraint类.
  • 运用Visual Format Language.

咱们在改动束缚的时分一般不会add/remove constraints, 而是active/deactivate.

运用Layout anchor

这个办法或许是最直观的一种办法.

// Get the superview's layout
let margins = view.layoutMarginsGuide
// Pin the leading edge of myView to the margin's leading edge
myView.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
// Pin the trailing edge of myView to the margin's trailing edge
myView.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
// Give myView a 1:2 aspect ratio
myView.heightAnchor.constraint(equalTo: myView.widthAnchor, multiplier: 2.0).isActive = true

这儿咱们把每一条束缚设置了isActive = true.

也能够直接放在一个数组里一起activate, 会有功能优势:

NSLayoutConstraint.activate([
    myView.leadingAnchor.constraint(equalTo: margins.leadingAnchor),
    myView.trailingAnchor.constraint(equalTo: margins.trailingAnchor),
    myView.heightAnchor.constraint(equalTo: myView.widthAnchor, multiplier: 2.0)
])

运用NSLayoutConstraint

运用NSLayoutConstraint写起来比较烦琐, 必须给每个参数都指定值:

NSLayoutConstraint(item: myView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leadingMargin, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: myView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailingMargin, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: myView, attribute: .height, relatedBy: .equal, toItem: myView, attribute:.width, multiplier: 2.0, constant:0.0).isActive = true

这个不但写起来费事, 可读性也很差.

Visual Format Language (VFL)

let views = ["myView" : myView]
let formatString = "|-[myView]-|"
let constraints = NSLayoutConstraint.constraints(withVisualFormat: formatString, options: .alignAllTop, metrics: nil, views: views)
NSLayoutConstraint.activate(constraints)

用一些键盘符号来表达这个布局的. (like a way of drawing the layout you want with a series of keyboard symbols)

管道符号代表parent view的边边.

Auto Layout的作业原理

[Android开发学iOS系列] Auto Layout

图来自于: developer.apple.com/videos/play…

Render loop包含如上三个阶段:

  • update constraints从叶子节点向上.
  • layout从parent节点向下执行.
  • display即最终的绘制阶段.

这三个阶段对应的办法:

[Android开发学iOS系列] Auto Layout

Update Constraints

它的作业是:

  • 把每个公式(束缚)加入核算引擎Engine里.
  • 核算引擎负责解出变量: 最终的frame.
  • 通知View: Superview: setNeedsLayout().

engine这儿扮演一个layout cache和tracker. 收到改动时它会重新核算.

Layout

从engine得到信息后, Subview setBounds(), subview setCenter().

尺度和优先级

了解了Auto Layout的原理之后, 看尺度和优先级的部分就很好了解.

Intrinsic content size

有一些View有固有内容尺度, 关于AutoLayout来说, 会默许运用intrinsic content size, 这样开发者就不必非得提供尺度信息.

默许运用: intrinsic content size. 固有内容尺度.

  • UIImageView: image size.
  • UILabel: text size.

优先级

优先级的值能够从1到1000, 默许是1000.

  • Required: 1000
  • Default High: 750
  • Default Low: 250

有优先级是因为多个constraints之间或许会有冲突, 那么束缚的要求或许不能完全100%满意, 核算引擎会在在不能满意的情况下, 尽量地削减偏差.

束缚的优先级就用来表明哪条束缚咱们更加关怀, 更想满意, 优先考虑.

优先级相关的变量

  • content hugging priority: 尺度比固有内容更大的或许性. 默许250. 值越小表明View更愿意扩张来满意束缚了; 值越大表明View希望尽或许地挨近固有尺度.
  • content compression resistance priority: 尺度比固有内容尺度更小的阻力程度. 默许750. 值越大表明这个View紧缩内容的或许性越小.

Auto Layout的运用细则

Properties & Functions

有个重要的特点要提一下:

  • translatesAutoresizingMaskIntoConstraints

这个特点是为了兼容Auto Layout呈现之前的根据frame布局的legacy layout体系, 协助View在Auto Layout的国际里, 以legacy layout system的办法运作.

当这个特点为true, 而且设置了frame时, 引擎会主动生成constraints来满意这个frame.

这个View的特点默许为true. 当咱们要用constraints时需求设置为false.

  • 当在storyboard中开端为View设置constraints时, 会主动设置为false.
  • 当咱们在代码中给view设置束缚之前, 需求自己显式地把这个特点设置为false.

假如还是用frame布局, 这个特点不必设置成false. 比方在循环里生成许多view的时分, 或许想有一些尺度和方位用frame设置.

  • sizeToFit(): 刚好包裹内容的巨细.

Stack View

Stack View是在Auto Layout的基础上的, 协助咱们做一些水平或许笔直的布局, 不必写内部元素间的constraints. (类似于Android中的LinearLayout.)

往Stack View里加需求叠放的元素用的是addArrangedSubview()这个办法.

与此同时, addSubview()办法能够用来加一些别的View.

几个特点:

  • axis: 主轴方向.
  • alignment: 对齐办法.
  • distribution: 沿着主轴的分布.

Stack View是比较轻量的, 所以官方会建议尽量多运用Stack View, 只在有必要的时分写束缚. 的确便利许多.

Layout Guide

许多时分为了布局的需求咱们或许要包裹View或许是添加一下辅助View, 每个View都有自己的layer, 所以为了改善功能, 咱们能够运用Layout Guide.

View自带一个layoutMarginsGuide.

还挺便利的. (看了这个视频: www.youtube.com/watch?v=4qP…)

Performance & Building Efficient Layouts

iOS12对AutoLayout的功能做了许多改善, 这个WWDC的talk有讲.

关于有效率的布局, 简而言之就是少做无用功.

Constraint Churn

constraint churning是个典型的功能问题. churn: 搅动.

constraint churn是指更新了constraints, 但实际上view并不需求移动.

这样是给engine发送了额定的信息, 达到必定数量之后, 就会影响功能.

需求注意的是:

  • 不要remove all constraints然后又add all. 能够把它们分组, 哪些是固定不变的, 那么addView的时分就加上, 然后activate; 关于需求动态改动的部分能够分两组(比方一个依据内容动态决议是否需求显示图片的例子, 能够有两个数组: imageConstraints和noImageConstraints), 单独activate/deactivate这两组束缚.
  • 运用isHidden能够进步效率. 比起add/remove Subview来说.

也是WWDC2018/220里提到的, 怎样避免Constraint Churn:

  • Avoid removing all constraints
  • Add static constraints once
  • Only change the constraints that need changing
  • Hide views instead of removing them

Size

能够挑选性地override一些尺度, 削减text measure核算的过程:

  • Return size if known without text measurement
  • Use UIView.noIntrinsicMetric and constraints.

System Layout Size Fitting Size

intrinsic content size是view传给engine的.

而这个system layout size fitting size, 是从engine取出来的.

可是它有想不到的功能耗费. (every time you call the method, an engine is created and discarded.)

Debugging

Auto Layout中由束缚引起的错误或许会有:

  • 束缚自相矛盾(冲突), 不能满意, 无解. (比方一个宽度既等于100又等于200, ???)
  • 束缚缺乏导致有许多或许的解. (Engine会给出一个解, 但或许不是你想要的.)

关于怎样debug能够看: developer.apple.com/library/arc…

大体上是依据Log还有一些或许有协助的view的特点和办法(供debug用).

这个视频(developer.apple.com/videos/play…)的后半段有讲debug.

这儿还有一个小工具网站: www.wtfautolayout.com/

Summary

Auto Layout是线性代数的应用实例.

有时分搬砖搬久了是不是应该慢下来赏识一下数学的美.

References

  • Understanding Auto Layout – Official Doc
  • High Performance Auto Layout – WWDC2018
  • Mysteries of Auto Layout, Part 1 – WWDC 2015
  • Mysteries of Auto Layout, Part 2 – WWDC 2015
  • Auto Layout Basics at codepath
  • The Auto Layout cheat sheet
  • Behind the Scenes with Auto Layout – iOS Conf SG 2019
  • AutoLayout Log剖析小工具