Swift 中使用代码完成自动布局

autolayout

在前篇Autolayout 介绍中,大致通过Interface Builder的方式来使用Autolayout。但并不是所有情况都能使用IB的,比如说要写一个通用的组件,代码的方式明显更胜一筹。

NSLayoutConstraint

代码模式,通过 NSLayoutConstraint init(item:attribute:relatedBy:toItem:attribute:multiplier:constant:) 方法来设置Constraint。

和IB中的设置相同,通过设置2个控件之前的关系和值来调整其位置。如果是控件自身属性的设置,像宽高,相对对象设置为nil即可,见示例中的height部分。

Relation是约束的判断条件(相等,小于还是大于)

  1. NSLayoutRelationEqual
  2. NSLayoutRelationLessThanOrEqual
  3. NSLayoutRelationGreaterThanOrEqual

如果需啊对某个View启用代码布局,一定要设置这个属性,不然Constraint不生效。

1
tmpView.setTranslatesAutoresizingMaskIntoConstraints(false)

使用代码来实现Constraint时,你会发现就像下面一样,40行的代码只是为了设定一个View的上下左右边距。如果有多个,疯了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// top
let topConstraint = NSLayoutConstraint(
item: self.tabView,
attribute: NSLayoutAttribute.Top,
relatedBy: NSLayoutRelation.Equal,
toItem: self.topLayoutGuide,
attribute: NSLayoutAttribute.Bottom,
multiplier: 1.0,
constant: 0)
self.view.addConstraint(topConstraint)
// left
let leadmingConstraint = NSLayoutConstraint(
item: self.tabView,
attribute: NSLayoutAttribute.Leading,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Leading,
multiplier: 1.0,
constant: 0)
self.view.addConstraint(leadmingConstraint)
// right
let trailingConstraint = NSLayoutConstraint(
item: self.tabView,
attribute: NSLayoutAttribute.Trailing,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Trailing,
multiplier: 1.0,
constant: 0)
self.view.addConstraint(trailingConstraint)
// height *add to itself
let heightConstraint = NSLayoutConstraint(
item: self.tabView,
attribute: NSLayoutAttribute.Height,
relatedBy: NSLayoutRelation.Equal,
toItem: nil,
attribute: NSLayoutAttribute.NotAnAttribute,
multiplier: 1.0,
constant: 40)
self.tabView.addConstraint(heightConstraint)

计算公式

1
attribute1 == multiplier x attribute2 + constant

Visual Format Language 来解救

第一眼看到VFL的语法时,感觉整个人都不好了,不过花时间学习下来之后,还是很清晰的。

语法及示例

官方语法示例

  1. ####方向
    对于一个View来说,只有2个方向 (横向 Horizontal / 竖向 Vertical),默认为H。
  2. ####数值
    -10- 表示2个控件之间的间距,(100) 用来表示一个对象自己的大小。
  3. ####优先级
    @表示优先级
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 相对于父View
// 水平方向 左右据父元素各0
H:|-0-[testView]-0-|
// 水平方向,左边距父4个像素,并且宽为100(宽的优先级750)
H:|-4-[testView(100@750)]
// 水平方向,View宽200,左右边距各大于20
|-(>=20)-[view(==200)]-(>=20)-|
// 垂直方向,上下各50像素边距
V:|-50-[testView]-50-|
// 垂直方向,上50边距,View高为40
V:|-50-[testView(40)]
// 垂直方向,上50边距,下50边距且优先级为981,高100且980的优先级。(此时应以下边距50为准)
V:|-50-[testView(100@980)]-50@981-|
# 两个View之间
两个View之间的关系只有3种, 等于 == / 大于 >= / 小于 <=
// 等宽
H:[view1(==view2)]
// 垂直间距
V:[view1]-10-[view2]
# 单个View
// 宽度大于70,小于100(多个条件)
[view1(>=70,<=100)]

代码示例

代码模式,通过 NSLayoutConstraint constraintsWithVisualFormat(_:options:metrics:views:) 方法来设置Constraint。

  • format VFL字串
  • options 暂时没用到
  • metrics 定义了一些VFL中要用的参数。
  • views 需要constraint关系的所有view。(也可以是一个)
1
2
3
4
5
6
var horizontal = NSLayoutConstraint.constraintsWithVisualFormat("H:|-4-[testView2(100@750)]", options: NSLayoutFormatOptions(0), metrics: nil, views: bindings)
self.view.addConstraints(horizontal)
var vertical = NSLayoutConstraint.constraintsWithVisualFormat("V:|-50-[testView2(100@980)]-50@981-|", options: NSLayoutFormatOptions.allZeros, metrics: nil, views: bindings)
self.view.addConstraints(vertical)

Question:

####Q: 如何调试?

####Q: 如何使用iOS8中的Margin

iOS 8中引入了layoutMargins的概念。

constraintWithItem中可以使用NSLayoutAttribute.TopMargin这样的attribute来设置。

VFL中还没找到相应文档。目测margin可以自己通过设置边距得到。

REF::