原理:IOS6.0 之后,苹果优化了UI界面的布局方式,提出了自动布局的概念,和之前的autoresizing相比功能更强大。子视图基于父视图的自动布局显示。都是父视图去添加对子视图的约束。
在这里主要说的是通过代码对自动布局视图的实现。
代码中一般用到的有两个添加约束的方式:
1.- (void)addConstraint:(NSLayoutConstraint *)constraint NS_AVAILABLE_IOS(6_0);
2.- (void)addConstraints:(NSArray *)constraints NS_AVAILABLE_IOS(6_0);
<
在使用自动布局之前要对子视图的布局方式进行调整,用到这个UIView的属性。
- (BOOL)translatesAutoresizingMaskIntoConstraints NS_AVAILABLE_IOS(6_0); // Default YES
需要将其设置为NO;
>
下面用简单例子说明一下:
(1) – 按比例缩放和优先级
首先说按比例缩放,这是在Interface Builder中无法设置的内容。而在代码中,使用NSLayoutConstraint类型的初始化函数中的multiplier参数就可以非常简单的设置按比例缩放。同时也可以设置不同NSLayoutAttribute参数来达到意想不到的效果,比如“A的Width等于B的Height的2倍”这样的效果。
1 topLabel= [[UILabel alloc] initWithFrame:CGRectZero]; 2 topLabel.translatesAutoresizingMaskIntoConstraints = NO; 3 topLabel.backgroundColor = [UIColor lightGrayColor]; 4 topLabel.numberOfLines = 0; 5 topLabel.text = @"选我选我选我选我选我选我"; 6 topLabel.userInteractionEnabled = YES; 7 UITapGestureRecognizer *tt = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(nextContro)]; 8 [topLabel addGestureRecognizer:tt]; 9 [self.view addSubview:topLabel]; 10 11 secondLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 12 secondLabel.translatesAutoresizingMaskIntoConstraints = NO; 13 secondLabel.backgroundColor = [UIColor cyanColor]; 14 secondLabel.text = @"按我"; 15 secondLabel.userInteractionEnabled = YES; 16 UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(changeThirdLabel:)]; 17 [secondLabel addGestureRecognizer:tap]; 18 [self.view addSubview:secondLabel]; 19 20 thirdLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 21 thirdLabel.translatesAutoresizingMaskIntoConstraints = NO; 22 thirdLabel.backgroundColor = [UIColor purpleColor]; 23 thirdLabel.numberOfLines = 0; 24 thirdLabel.text = str; 25 [self.view addSubview:thirdLabel];
设置firstLabel的布局
1 NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:topLabel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:1 constant:0]; 2 NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:topLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:0.2 constant:0]; 3 4 [self.view addConstraint:widthConstraint]; 5 [self.view addConstraint:heightConstraint];
设置secondLabel的位置
1 //设置view之间长度相等,并且在试图在边缘内(超过边缘自动往下) 2 [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[topLabel][secondLabel(==topLabel)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(topLabel, secondLabel)]]; 3 4 //设置高度,距顶部20,之间间隔10,secondLabel高度为30,只是上面对齐下面不对齐 5 [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-20-[topLabel(>=20@400)]-10-[secondLabel(==30)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(topLabel, secondLabel)]];
注:这里不管是在format字符串里还是在binding字典里,不能使用self.label这样的字样
设置thirdLabel的位置并根据文本动态显示高度
1 //水平居中,始终距离父View底部20单位的距离。然后高度是父View高度的5分之一 2 //居中 3 [self.view addConstraint:[NSLayoutConstraint constraintWithItem:thirdLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]]; 4 //距离底部50,注意NSLayoutConstraint创建的constant是加在toItem参数的,所以需要-20。 5 [self.view addConstraint:[NSLayoutConstraint constraintWithItem:thirdLabel attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1 constant:-50]]; 6 //定义高度是父View的5分之一 7 // [self.view addConstraint:[NSLayoutConstraint constraintWithItem:thirdLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:0.5 constant:0]]; 8 9 NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:thirdLabel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:1 constant:0]; 10 constraint.priority = UILayoutPriorityDefaultHigh; 11 [self.view addConstraint:constraint]; 12 //最小高度为150 13 [thirdLabel addConstraint:[NSLayoutConstraint constraintWithItem:thirdLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeHeight multiplier:1 constant:150]]; 14 15 //注册KVO方法 16 [thirdLabel addObserver:self forKeyPath:@"bounds" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial context:nil];
//KVO回掉 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (object == thirdLabel && [keyPath isEqualToString:@"bounds"]) { // thirdLabel.text = str; // thirdLabel.text = @"ha"; // thirdLabel.text = NSStringFromCGSize(thirdLabel.bounds.size); } }
可以设置空间展示的动画效果
[UIView animateWithDuration:0.5 animations:^{ [self.view layoutIfNeeded]; }];
(2) – intrinsicContentSize和Content Hugging Priority
先说intrinsicContentSize,也就是控件的内置大小。比如UILabel,UIButton等控件,他们都有自己的内置大小。控件的内置大小往往是由控件本身的内容所决定的,比如一个UILabel的文字很长,那么该UILabel的内置大小自然会很长。控件的内置大小可以通过UIView的intrinsicContentSize属性来获取内置大小,也可以通过invalidateIntrinsicContentSize方法来在下次UI规划事件中重新计算intrinsicContentSize。如果直接创建一个原始的UIView对象,显然它的内置大小为0。
继续用代码来写Autolayout,先写一个辅助方法来快速设置UIView的边距限制:
1 //设置Autolayout中的边距辅助方法 2 - (void)setEdge:(UIView*)superview view:(UIView*)view attr:(NSLayoutAttribute)attr constant:(CGFloat)constant 3 { 4 [superview addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:attr relatedBy:NSLayoutRelationEqual toItem:superview attribute:attr multiplier:1.0 constant:constant]]; 5 }
接下来,创建一个UIView,利用上面的辅助方法快速设置其在父控件的左,上,右边距为20单位。UIView默认是没有intrinsicContentSize的。我们可以通过创建一个自定义的UIView来改写intrinsicContentSize。
在MyView里添加:
- (CGSize)intrinsicContentSize { return CGSizeMake(0, 100); //controll设置了left以及right,故这里的width没效果 }
如下代码:
1 //view1 2 MyView *view1 = [[MyView alloc] init]; 3 view1.backgroundColor = [UIColor yellowColor]; 4 //不允许AutoresizingMask转换成Autolayout 5 view1.translatesAutoresizingMaskIntoConstraints = NO; 6 [self.view addSubview:view1]; 7 //设置左,上,右边距为20. 8 [self setEdge:self.view view:view1 attr:NSLayoutAttributeLeft constant:20]; 9 [self setEdge:self.view view:view1 attr:NSLayoutAttributeTop constant:80]; 10 [self setEdge:self.view view:view1 attr:NSLayoutAttributeRight constant:-20]; 11 12 13 //view2 14 MyView *view2 = [MyView new]; 15 view2.backgroundColor = [UIColor greenColor]; 16 //不允许AutoresizingMask转换成Autolayout 17 view2.translatesAutoresizingMaskIntoConstraints = NO; 18 [self.view addSubview:view2]; 19 //设置左,下,右边距为20. 20 [self setEdge:self.view view:view2 attr:NSLayoutAttributeLeft constant:20]; 21 [self setEdge:self.view view:view2 attr:NSLayoutAttributeBottom constant:-80]; 22 [self setEdge:self.view view:view2 attr:NSLayoutAttributeRight constant:-20];
1 //设置两个View上下间距为20 2 [self.view addConstraint:[NSLayoutConstraint constraintWithItem:view2 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:view1 attribute:NSLayoutAttributeBottom multiplier:1.0 constant:20]];
设置view的Content Hugging Priority.Content Hugging Priority代表控件拒绝拉伸的优先级。优先级越高,控件会越不容易被拉伸。
还有一个Content Compression Resistance Priority代表控件拒绝压缩内置空间的优先级。优先级越高,控件的内置空间会越不容易被压缩。而这里的内置空间,就是上面讲的UIView的intrinsicContentSize。
可以直接通过UIView的setContentHuggingPriority:forAxis方法来设置控件的Content Hugging Priority,其中forAxis参数代表横向和纵向,本例中只需要设置纵向,所以传入UILayoutConstraintAxisVertical。整句代码:
[view2 setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
关于constraintsWithVisualFormat:函数介绍:
constraintsWithVisualFormat:参数为NSString型,指定Contsraint的属性,是垂直方向的限定还是水平方向的限定,参数定义一般如下:
V:|-(>=XXX) :表示垂直方向上相对于SuperView大于、等于、小于某个距离
若是要定义水平方向,则将V:改成H:即可
在接着后面-[]中括号里面对当前的View/控件 的高度/宽度进行设定;
options:字典类型的值;这里的值一般在系统定义的一个enum里面选取
metrics:nil;一般为nil ,参数类型为NSDictionary,从外部传入 //衡量标准
views:就是上面所加入到NSDictionary中的绑定的View
在这里要注意的是 AddConstraints 和 AddConstraint 之间的区别,一个添加的参数是NSArray,一个是NSLayoutConstraint
使用规则
|: 表示父视图
-:表示距离
V: :表示垂直
H: :表示水平
>= :表示视图间距、宽度和高度必须大于或等于某个值
<= :表示视图间距、宽度和高度必须小宇或等于某个值
== :表示视图间距、宽度或者高度必须等于某个值
@ :>=、<=、== 限制 最大为 1000
1.|-[view]-|: 视图处在父视图的左右边缘内
2.|-[view] : 视图处在父视图的左边缘
3.|[view] : 视图和父视图左边对齐
4.-[view]- : 设置视图的宽度高度
5.|-30.0-[view]-30.0-|: 表示离父视图 左右间距 30
6.[view(200.0)] : 表示视图宽度为 200.0
7.|-[view(view1)]-[view1]-| :表示视图宽度一样,并且在父视图左右边缘内
8. V:|-[view(50.0)] : 视图高度为 50
9: V:|-(==padding)-[imageView]->=0-[button]-(==padding)-| : 表示离父视图的距离
为Padding,这两个视图间距必须大于或等于0并且距离底部父视图为 padding。
10: [wideView(>=60@700)] :视图的宽度为至少为60 不能超过 700
11: 如果没有声明方向默认为 水平 V: