iOS - AutoLayout -1

164 阅读4分钟

1、AutoLayout

自动布局(AutoLayout)是iOS6引入的关系布局,实现动态位置和多视图关系的布局方式,是对frame布局和AutoresizingMask的不足进行补充的一种方式,现在已经成为主流的布局方案,由于原始创建方式比较复杂,可以使用优秀的第三方框架方便创建约束(Swift: SnapKit, Objective-C: Masonary)。 AutoLayout,是一个基于约束,动态计算视图大小和位置的库,以布局引擎系统Layout Engine为核心,采用了 Cassowary布局算法,Layout Engine统一管理了布局的创建、更新、销毁,将视图的约束、优先级、固定大小通过计算转换成最终的大小和位置。 在Layout Engine中,每当约束发生变化,会重新计算布局,获取到布局后调用superview.setNeedLayout(),然后触发Deffered Layout Pass做容错处理,然后Layout Engine会从上到下调用layoutSubviews()来确定各子视图的位置(通过Cassowary算法计算),算出来后将子视图的frame从Layout Engine里拷贝出来,然后进行绘制、渲染,得到我们眼中看到的UI效果。

cassowary算法

1997年,一个名叫 Cassowary 的布局算法解决了用户界面的布局问题,它通过将布局问题抽象成线性等式和不等式约束来进行求解。 Cassowary能够有效解析线性等式系统和线性不等式系统,用来表示用户界面中那些相等关系和不等关系。基于此,Cassowary开发了一种规则系统,通过约束来描述视图间的关系。约束就是规则,这个规则能够表示出一个视图相对于另外一个视图的位置。

由于Cassowary算法让视图位置可以按照一种简单的布局思路来写,这些简单的相对位置描述可以在运行时动态的计算出视图具体的位置。视图位置写法简化,界面相关代码也就更易于维护。苹果公司也是看中了这一点,将其引入了自己的系统中。

AutoLayout的性能

Auto Layout 进行布局时,可以指定一系列的约束,比如视图的高度、宽度等等。而每一个约束其实都是一个简单的线性等式或不等式,整个界面上的所有约束在一起就明确地(没有冲突)定义了整个系统的布局。

在涉及冲突发生时,AutoLayout 尽量满足最多并且优先级最高的约束, break一些优先级低的约束

在iOS12之前,AutoLayout在处理多层级嵌套时,开销呈指数级跃增,但是从iOS12开始,苹果补齐了这一漏洞,所以在iOS12及以后,我们就开始放心的使用AutoLayout,更多关于AutoLayout的性能问题及解决方法,请查看WWDC2018-202

####AutoLayout布局原理和语法

AutoLayout是通过约束来实现布局的。一个view一旦使用了AutoLayout约束,那么它的frame将永远都是0

在使用AutoLayout之前需要两个准备工作

  • 设置translatesAutoresizingMaskIntoConstraints为false
  • 如果是viewControl则AutoLayout适配写在
  • (void)updateViewConstraints中。 如果是view则AutoLayout适配写在
  • (void)updateConstraints中。实际上,这也正是AutoLayout好处之一,可以集中将一个controller和view的适配在一个方法中。

####参数

 let constraint = [NSLayoutConstraint (item: <#T##Any#>, attribute: <#T##NSLayoutConstraint.Attribute#>, relatedBy: <#T##NSLayoutConstraint.Relation#>, toItem: <#T##Any?#>, attribute: <#T##NSLayoutConstraint.Attribute#>, multiplier: <#T##CGFloat#>, constant: <#T##CGFloat#>)]

创建一个AutoLayout需要七个参数,他们分别是

(1)Item:被约束对象 (2)第一个attribute:被约束对象的关系 (3)relatedBy:约束描述 (4)toItem:约束源 (5)第二个attribute:约束源的关系 (6)multiplier:约束系数 (7)constant:约束常数

  • 其中 attributebute 对应的属性如下
public enum Attribute : Int {        
        case left = 1
        case right = 2
        case top = 3
        case bottom = 4
        case leading = 5
        case trailing = 6
        case width = 7
        case height = 8
        case centerX = 9
        case centerY = 10
        case lastBaseline = 11
        @available(iOS 8.0, *)
        case firstBaseline = 12
        @available(iOS 8.0, *)
        case leftMargin = 13
        @available(iOS 8.0, *)
        case rightMargin = 14
        @available(iOS 8.0, *)
        case topMargin = 15
        @available(iOS 8.0, *)
        case bottomMargin = 16
        @available(iOS 8.0, *)
        case leadingMargin = 17
        @available(iOS 8.0, *)
        case trailingMargin = 18
        @available(iOS 8.0, *)
        case centerXWithinMargins = 19
        @available(iOS 8.0, *)
        case centerYWithinMargins = 20
        case notAnAttribute = 0
    }

其中leading 、trailing 是表示前、后, left、right 表示左右,某种意义上leading == left,trailing == right,但是当旋转的时候会出现一些问题,所有建议大家使用 leading 、trailing

  • relatedBy 对应的属性如下
public enum Relation : Int {
        case lessThanOrEqual = -1
        case equal = 0
        case greaterThanOrEqual = 1
    }

约束描述主要就是<= 、==、 >= 主要是用于不确定大小和坐标的约束

  • 约束系数multiplier和约束常数constant主要是计算约束关系最终结果的,遵循公式"view1.attr1 = view2.attr2 * multiplier + constant"

想准确的将一个View动态布局,有时候往往需要设置好几个约束来定位view的位置,所以这种代码写起来往往比设置frame更加的冗长,所以就有了AutoLayout的出现。

未完待续。。。