如何编写高性能的 Auto Layout

对于常常写 UI 页面的 iOS 开发者来讲,Auto Layout 是提升开发效率的一大利器。但若是使用不当的话,也会对项目性能形成损害。因此,知道如何正确的使用 Auto Layout 仍是很重要的。markdown

本文首先会经过一个例子来讲明下什么是约束流失,了解约束流失后,会带你们看一下 Render Loop 的工做流程。而后会说下 Auto Layout 的背后实现原理。最后,了解下 Auto Layout 特定状况下的最佳作法。让咱们开始吧。ide

约束流失

什么是约束流失?oop

答:对于同一视图,进行没必要要的删除和从新添加约束。布局

经过下面的例子来解释一下:性能

var myConstraints = [NSLayoutConstraint]()
let text1 = UILabel()
let text2 = UILabel()

override func updateViewConstraints() {
    // step1
    NSLayoutConstraint.deactivate(myConstraints)
    myConstraints.removeAll()
    // step2
    let views = ["text1": text1, "text2": text2]
    myConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-[text1]-[text2]",
                                                    options: [.alignAllFirstBaseline],
                                                    metrics: nil,
                                                    views: views)
    
    myConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-[text1]-|",
                                                    options: [.alignAllFirstBaseline],
                                                    metrics: nil,
                                                    views: views)
    // step3
    NSLayoutConstraint.activate(myConstraints)
    super.updateViewConstraints()
}
复制代码

上面的代码作了三件事:spa

  • step1:将以前的约束失效并移除。
  • step2:从新设置约束。
  • step3:生效从新设置的约束。

移除,从新建立。直观上看这不是高效的代码,事实上也确实不是。它会给性能带来压力,由于这段代码会每秒执行不少次。code

咱们能够经过一个简单的 if 判断来避免无用的执行。orm

override func updateViewConstraints() {
    if myConstraints.isEmpty {
        var constrains = [NSLayoutConstraint]()
        
        let views = ["text1": text1, "text2": text2]
        constrains += NSLayoutConstraint.constraints(withVisualFormat: "H:|-[text1]-[text2]",
                                                        options: [.alignAllFirstBaseline],
                                                        metrics: nil,
                                                        views: views)
        
        constrains += NSLayoutConstraint.constraints(withVisualFormat: "V:|-[text1]-|",
                                                        options: [.alignAllFirstBaseline],
                                                        metrics: nil,
                                                        views: views)
        NSLayoutConstraint.activate(constrains)
        myConstraints = constrains
    }
    super.updateViewConstraints()
}
复制代码

在更新约束的时候,首先判断当前约束是否有值,如有值则直接跳过;无值再建立约束赋值。这样就避免了屡次执行移除,从新建立的流程。开发

Render Loop 概览

Render Loop 涉及更新约束、布局、显示三个阶段。下面是这三个阶段的流向: 截屏2021-05-11 下午3.23.31.pngrem

更新布局是从底层视图一步步流向 window ,而布局恰巧相反,从 window 流向底层视图,显示则和布局流向一致。

Render Loop 的优势:能够避免无用的工做;注意事项:会运行不少次。因此咱们应该谨慎使用。

Auto Layout 的背后实现

当咱们给控件添加约束时,下面的四个值必须能计算出来,不然视图会显示不正常。

控件需计算的四个值:minX、minY、width、height。

截屏2021-05-11 下午3.32.38.png

好比上面的约束会替换成下面的公式:

  • text1
    • text1.minX = 20
    • text1.minY = 30
    • text1.width = 100
    • text1.height = 20
  • text2
    • text2.minX = text1.minX + text1.width + 10 最终得出 130
    • text2.minY = 30
    • text2.width = 100
    • text2.height = 20

也就是说 Auto Layout 就是用二元一次方程式来求出各个参数的值。

当咱们写下上面的约束时,系统会建立一个 Engine,Engine 去负责约束的计算,最终 Engine 会把 minX、minY、width、height 的具体值返回给 View,View 则根据返回值调用 setNeedsLayout() 来更新视图。

截屏2021-05-11 下午3.49.31.png

最佳作法

  • 特定状况下须要隐藏控件
    • 直接使用 hidden 属性就好,不要移除控件或者约束。
  • 刷新视图
    • 避免移除全部约束,最好在当前约束的基础上修改。
    • 对于不变的约束确保只添加一次。
    • 只修改须要改变的约束。

总结

  • 不要让约束流失。
  • 约束底层计算只是简单的方程式计算。
  • 只为你的功能耗费性能,不作无谓的消耗。
  • 避免添加有歧义的约束,好比要求 view 的 width 便是 50 又是 200。
相关文章
相关标签/搜索