iOS-Auto Layout[8]-Programmatically Creating Constraints

AutoLayout大部分情况下是在IB中设计的,如果需要在代码中设计,可以使用NSLayoutConstraintLayout AnchorsVisual Format Language三种方式来创造Constraints。

NSLayoutConstraint Class

直接通过NSLayoutConstraint Class的函数可以在相应的View上添加Constraints:

1
2
3
4
5
6
7
8
UIView *testView = [[UIView alloc] init];
testView.backgroundColor = [UIColor redColor];
[self.view addSubview:testView];
testView.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint constraintWithItem:testView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1.0f constant:0.0f].active = YES;
[NSLayoutConstraint constraintWithItem:testView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTrailing multiplier:1.0f constant:0.0f].active = YES;
[NSLayoutConstraint constraintWithItem:testView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0f constant:0.0f].active = YES;
[NSLayoutConstraint constraintWithItem:testView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:0.5f constant:0.0f].active = YES;

这里,要注意的是,在创建Constraints之前,需要将对应的View添加到View结构树中去:

1
[self.view addSubview:testView];

否则,会报错:

1
**Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with items <UIView: 0x7fe56a421c20; frame = (0 0; 0 0); layer = <CALayer: 0x7fe56a41c6e0>> and <UIView: 0x7fe56a426e80; frame = (0 0; 667 375); autoresize = W+H; layer = <CALayer: 0x7fe56a424fa0>> because they have no common ancestor.  Does the constraint reference items in different view hierarchies?  That's illegal.'

另外,需要设置translatesAutoresizingMaskIntoConstraints属性为NO

1
testView.translatesAutoresizingMaskIntoConstraints = NO;

否则,会报错:

1
2
3
4
5
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.

当出现约束错误时,NSLayoutConstraint Class不会在编译时报错,只会在运行时报错。

Layout Anchors

由于NSLayoutConstraint Class比较复杂,易读性较差,所以,iOS 9以上系统又提供了更便捷地创建Constraints方式,使用Layout Anchors

1
2
3
4
5
6
7
8
9
UIView *testView = [[UIView alloc] init];
testView.backgroundColor = [UIColor redColor];
[self.view addSubview:testView];
testView.translatesAutoresizingMaskIntoConstraints = NO;
UILayoutGuide *margins = self.view.layoutMarginsGuide;
[testView.leadingAnchor constraintEqualToAnchor:margins.leadingAnchor].active = YES;
[testView.trailingAnchor constraintEqualToAnchor:margins.trailingAnchor].active = YES;
[testView.topAnchor constraintEqualToAnchor:margins.topAnchor].active = YES;
[testView.heightAnchor constraintEqualToAnchor:testView.widthAnchor multiplier:0.5f].active = YES;

相比于NSLayoutConstraint Class,Layout Anchors不仅提供了更易读的方法,而且,还加入了类型信息,可以在编译时可以报错,而不是等到运行时。例如:在leadingAnchor和widthAnchor之间创建Constraints时,会报错。

Visual Format Language

Visual Format Language使用一个类似ASCII-art类型的字符串来定义Constratints,其有以下的优缺点:

  • Auto Layout打印在控制台中是Visual Format Language,因为编码如果使用Visual Format Language,将会更直观;
  • Visual Format Language可以使用很简短的表达式,一次性创建多个Constraitns;
  • Visual Format Language只能创建有效的Constraints;
  • 一些特殊的Constraints无法通过Visual Format Language创建,例如Aspect Ratios;
  • 编译器不会校验Visual Format Language的表达式,只能在运行时发现错误。

Visual Format Language的语法如下:

1
2
3
4
5
6
7
UIView *testView = [[UIView alloc] init];
testView.backgroundColor = [UIColor redColor];
[self.view addSubview:testView];
testView.translatesAutoresizingMaskIntoConstraints = NO;

[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[TestView(>=100)]-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:@{@"TestView":testView}]];
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-Height-[TestView]-Height-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:@{@"Height":@40} views:@{@"TestView":testView}]];

注意views要用NSDictionary定义好,在表达式中使用到的view:

1
views:@{@"TestView":testView}

matrics也是用NSDictionary定义好,在表达式中使用到的变量:

1
metrics:@{@"Height":@40}

表达式语法如下:

View用[]括起来表示,例如:

1
[TestView]

SuperVie用’|’表示,例如:

1
|-[TestView]-|

表达式前面用’H:’或者’V:’表示轴向,如果没有加上,默认是’H:’,例如:

1
2
|-[TestView]-|
V:|-[TestView]-|

标准间距(8px)用’-‘表示,例如:

1
|-[TestView]-|

表示TestView距离左右两边都是8px。

长度约束用’>=’、’==’、’<=’表示,例如:

1
2
3
|-40-[TestView(>=50)]-40-|
[TestView(==TestView2)]
[TestView(>=70, <=100)]

第一句表示TestView的Width至少为50,第二句表示TestView的Width等于TestView2的Width,第三句表示TestView的Width在70和100之间。

没有间距,可以直接并列,例如:

1
[TestView][TestView2]

优先级用‘@’加数字表示,例如:

1
[TestView(100@20)]

表示TestView的Width为100,优先级为20。

多个View的Constraints可以写在一条表达式中:

1
|-[TestView]-[TestView2]-[TestView3(>=20)]-|