校对:Ray_Xia
今天和大家来唠一唠关于 Autolayout 的那点事,若大家在阅读过程中发现错误请及时指正,共同进步哈!😄
Autolayout
一个 view 通常有三种 autolayout 的方式:
- 代码中为 view 添加 autolayout 约束,那么这个 view 将会使用 autolayout 来添加 constraint
- 在加载nib文件的时候,系统会检查是否勾选了 “Use Auto Layout” 选项, 如果勾选了则 xib 中的控件将会使用 autolayout
- 在你自定义的view中,如果它在类方法
requiresConstraintBasedLayout
中返回 true,则这个 view 会使用 autolayout 。 autolayout 存在这种方法的原因是由于你可能需要打开 autolayout 以便在代码中添加约束。
在代码中添加约束的一个通常的地方是 updateConstraint 这个方法。然而,如果 autolayout 没有打开,updateConstraint 这个方法不会被调用。所以 requiresConstraintBasedLayout 是打开 autolayout 的一种方法。
当一组相同层级的视图使用了 autolayout 则另外一组不可以使用,当 superview 使用了 autolayout 则它的 subviews 则不可以使用。autolayout 是通过 superview 链实现的,因此如果一个 view 使用了 autolayout 则它的 superview 也会自动的使用。如果这些 view 中有一个是 viewcontroller 中的 main view 则这个viewcontroller 会接收到相关 autolayout 的事件,否则将不会接受到事件。
注意
你不能在nib中关闭掉 autolayout ,所有的 nib 中的 view 需要使用 autolayout 或者 autoresizing。 如果你的 nib 中一部分使用 autoresizing 另外一部分使用 autolayout, 最好是将它们分别开来到不同的 nib 中 然后在 runtime 的时候将它们加载并结合。有兴趣的同学可以试一下,我一般不会将这两种方式结合使用。
Constraints
Autolayout 的 constraints 和 一般的 contraints 其实都是
NSLayoutContraint
的实例对象,它表明了一个 view 的高和宽,或者来描述两个view之间的属性关系,对于后者来讲,属性不一定要一样,两个view 之间并不必须是兄弟关系(即两个view拥有同一个superview)或者父子关系(superview和subview)。唯一的要求就是它们拥有一个相同的祖先(也就是说在view的层级中有一个最终的superview)
两个 view 的的属性都会有它们各自的 constraint, 如果 constraint 表明了一个 view 的长和宽,那另外一个 view 将会是 nil 其属性为.NotAnAttribute
,如果你不理解的话,不要着急,一会儿会有代码演示。其他的属性还有:
- .Top, .Bottom
- .Left, .Right, .Leading, .Trailing
- .Eidth, .Height
- .CenterX, CenterY
- .FirstBaseline, .LastBaseline
.FirstBaseline 主要适用于多行标签,从标签顶部向下一定的距离;.LastBaseline 是从标签的底部向上一定距离。
值得一提的是.leading
和trailing
整两个属性,其实就是 “左” 和 “右”的意思。在 iOS9 中,整个界面将会自动翻转,只有你使用leading
和trailing
约束时,界面才不会乱。
multiplier,constant
这两个数字会应用到第二个属性来决定第一个属性的值。multiplier会乘上第二个属性的值,然后加上constant的值就是第一个属性的值。
基本上可以用这个公式来解释:a1 = ma2+c,a1和a2是两个属性,m和c分别是乘数和常数。因此,要使第一个属性的值等于
第二个属性的值,那么 multiplier 设为1,常数设为0.如果你要将一个 view 的 width 和height 设为一个绝对值,那么,乘数要为1然后将 constant 设为
width 和 height 的值。
relation
一个
NSLayoutRelation
用改变multiplier
和constant
来描述两个属性之间的关系。这就是我在前面小段在等式中放入等式标记。等式标记可能是.Equal
,但也可以用非等式标记.LessThanOrEqual
,.GreaterThanOrEqual
priority
priority 的值在 1-1000 范围内,某些特定的标准有特定的 peiorities 与之对应。 约束可以有不同的优先级,来确定他们的顺序。
约束是属于它的 view 的,一个 view 可以有许多个约束, 每个 UIView 都含有一个
constraints
属性,以及下面的实例方法:
addConstraint:, addConstraints:
removeConstraint:, removeConstraints:
问题是所给定的约束到底属于哪个View。答案是:与这个约束相关的views中,在view层级中最近的那个view。如果可能的话,应该是views中的其中一个。 举个例子:如果一个 view 指定了 width 的宽度这个约束,这个约束就属于这个view;如果这个 view 设置了与 superview 顶部的约束,这个约束就属于这个 view 的 superview ,如果这个 view 与他的兄弟 view 对齐,那这个约束就属于他们共同的 superview 。
然而从 iOS8 开始,你可以用NSLayoutConstraint
类方法activateConstraints
来激活约束,而不是显式指定将约束给某个view。激活的约束将自动的添加到正确的 view 上面,这减少了开发者去决定到底该将约束赋给某个 view 。当然也要一个方法来去掉 view 的约束:deactivateConstraints
。一个约束有一个激活的属性,你可以将一个约束设为激活或者不激活,再加上它会告诉你之前添加的一个约束是否是界面的一部分。
NSLayoutConstraints 的属性都是只读属性,除了
priority
和constant
,如果你想改变已存在的约束, 你只能先将其移出然后再新添加一个constraints。
Autoresizing constraints
如果你机械的将一个视图设置为了自动布局,则其他视图也会变成自动布局,即使其他的视图之前并没有使用过自动布局。因此它们需要一种方式,当这个view变成自动布局的时候,要以自动布局的方式来决定这个view的位置和布局的约束,他们之前是通过
frame
和autoresizingMask
来决定的。在运行时,autolayout 会把 view 的frame
和autoresizing
设置到 constraints 中,其结果会生成一组隐式的约束来影响这个 view。多亏了这些隐式的约束,可以让 layout 在 view的autoreszingMask
的作用下继续工作
举个例子,假如我有一个 UILabel 它的frame设置为(20,20,42,22),并且设置它的autoreszingMask
为.None
。如果这个 label 使用了 autolayout, 则它的 superview 将会获取到 四个隐式的约束来设置它的长和宽以及 centerX,centerY。
刚才我们所说的都是在你把 view 的translatesAutoresizingMaskIntoConstraints
设置为 true 的情况下,事实上如果这个 view 是从代码或者从未勾选 "Use Auto Layout" 的 nib文件中实例化出来则translatesAutoresizingMaskIntoConstraints
的默认值都是 true。做一个猜想如果一个 view 的实例通过上面的方式得到的,你想让它的 frame 和 autoreszingMask 作为约束参与 autolayout。
需要注意的是如果你明确希望你的 view 只使用 autolayout 那你一定要记得设置translatesAutoresizingMaskIntoConstraints
为 false。如果你忘记设置了,那么隐式约束和你设置的显式约束都会影响到 view 这样将会出现你不希望看到的结果。并且会出现约束冲突,会在控制台报警告呦!
Creating constraints in code
在代码中为一个view添加约束要采用 NSLayoutConstraint 的初始化方法
init(item:at tri bute:related By:to Item:at - tri bute: mul ti plier:con stant:)
接下来我们将上一节Layout(Autoresizing)用Autoresizing实现的view重新用constraints实现一遍:
代码如下:
let v1 = UIView(frame: CGRectMake(100,111,132,194))
v1.backgroundColor = UIColor.magentaColor()
let v2 = UIView(frame: CGRectMake(0,0,132,10))
v2.backgroundColor = UIColor.greenColor()
let v3 = UIView(frame: CGRectMake(v1.bounds.width-20, v1.bounds.height-20,20,20))
v3.backgroundColor = UIColor.blueColor()
self.view.addSubview(v1)
v1.addSubview(v2)
v1.addSubview(v3)
v2.translatesAutoresizingMaskIntoConstraints = false
v3.translatesAutoresizingMaskIntoConstraints = false
v1.addConstraint(
NSLayoutConstraint(item: v2,
attribute: .Leading,
relatedBy: .Equal,
toItem: v1,
attribute: .Leading,
multiplier: 1,
constant: 0))
v1.addConstraint(
NSLayoutConstraint(item: v2,
attribute: .Trailing,
relatedBy: .Equal,
toItem: v1,
attribute: .Trailing,
multiplier: 1,
constant: 0))
v1.addConstraint(
NSLayoutConstraint(item: v2,
attribute: .Top,
relatedBy: .Equal,
toItem: v1,
attribute:.Top,
multiplier:1,
constant: 0))
v2.addConstraint(
NSLayoutConstraint(item:v2 ,
attribute: .Height,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1,
constant: 10))
v3.addConstraint(NSLayoutConstraint(item: v3,
attribute: .Width,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1,
constant: 20))
v3.addConstraint(NSLayoutConstraint(item: v3,
attribute: .Height,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1,
constant: 20))
v1.addConstraint(NSLayoutConstraint(item: v3,
attribute: .Trailing,
relatedBy: .Equal,
toItem: v1,
attribute: .Trailing,
multiplier: 1,
constant: 0))
v1.addConstraint(NSLayoutConstraint(item: v3,
attribute: .Bottom,
relatedBy: .Equal,
toItem: v1,
attribute: .Bottom,
multiplier: 1, constant:0))
贴出了这么一大块代码,可能你看上去觉得很复杂,并且很冗余,如果你是初学者希望你耐下性子看下去,你会发现 autolayout 真的要比 autoresizing 简单易懂,并且表述出来的意思也要比 autoresizing 更加清晰。并且 autolayout 能够表述出 autoresizing 不能做的事情,比如,我们现在设置v2的高度是v1高度的十分之一,并且无论v1的高度怎么变v2的高度都是v1的十分之一,如果不用autolayout做的话,则必须在代码中实现
layoutSubviews
方法。那么工作量又提高了一个重量级。
ok,以上就是前三天学习到的关于Autolayout方面的知识点,以前只晓得用但并不了解其中的原理和更深层的含义。经过这几天的学习真的是受益匪浅,关于layou方面的知识将会不断更新,敬请期待呦!