一、简介
AutoLayout是在WWDC2012被引入到iOS中,从iOS6.0后开始支持,随着苹果手机各种机型不断增多,急切希望能找到一种在界面约束布局中能够很好使用的方式来解决适配问题。目前,主要使用的约束布局主要有三种:
- Interface Builder(IB) 可以根据需求进行自定义
- 使用NSLayoutConstraint类代码创建(包括第三方类库Masonry的使用)
- 使用可视化格式语言VFL(Visual format language)来表示各项是如何沿着垂直和水平坐标轴布局
本文重点介绍第三种VFL的使用,探究可视化约束在视觉上有何种效果,如何创建它们以及如何在项目中使用它们。将学到度量字典和约束选项如何扩展可视化格式,已获得更多的灵活性。还将看到大量演示这些格式的事例,并探究他们产生的结果。
注意:要始终牢记所有的约束都是NSLayoutConstraint类的成员,无论你是以何种形式创建它们,每个约束都在一个OC对象中存储** y=mx+b **规则,并且通过AutoLayout引擎来表达该规则。可视化格式是另一种实现相同效果的工具。
二、可视化格式约束介绍
1.API介绍
(1)NSLayoutConstraint
+ (NSArray *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(NSDictionary *)metrics views:(NSDictionary *)views;
参数介绍:
- format:此参数为你的vfl语句,比如:@"H:|-[button]-|"
- opts:枚举参数,默认写0,具体跟据你所实现的需求去选择你想要的枚举
- metrics:这里是一个字典,当在format中使用了动态数据比如上现这句:@"H:|-[button(==width)]-|",表示这个button的宽度为width,那么这个参数去哪里找呢?就是在这个字典里面找到key对就的值,如果没有找到这个值,app就会crash.
- views:顾名思义,这是传所有你在vfl中使用到的view,那在上面这句例子中的应该怎么传呢?结果是这样的:NSDictionaryOfVariableBindings(button).如果你使用到了多个view,就可以这样 NSDictionaryOfVariableBindings(button,button1,button3...), 这个名字也要跟参数format中的一一对应,缺一不可.
(2)UIView API
- (void)addConstraints:(NSArray *)constraints;
在上面(1)中返回值类型是NSArray,而现在这个方法的参数也刚好是一个NSArray类型。那么直接把上一个方法的返回值当作这个方法的参数就可以了。如果你有多个VFL,你也可以利用可变数组( NSMutableArray)把这多个VFL返回的数据拼在一起,然后再调用addConstraints:方法。
2.简单使用
(1)对于单控件
UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
[btn1 setTitle:@"按钮1" forState:UIControlStateNormal];
// 注意这句话必须添加 如果没有这一行,约束将不生效,控制台会输出一连串的错误.
btn1.translatesAutoresizingMaskIntoConstraints = NO;
[btn1 setBackgroundColor:[UIColor redColor]];
[self.view addSubview:btn1];
NSArray *constraints1 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[btn1]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(btn1)];
/*
这里的意思是:btn1在水平方向上距离它的superView,左右各20px,比如在这里他的大小就是
320-20*2=280.在@"H:|-[btn1]-|"这个语句中,其中"H:"是表示这是水平方向上的约束,"|"是表示
superView,"-"表示一个间隔空间,这个间隔如果是如superView之间的,那么就是20px,如果是两个同
级别的view,比如@"[button]-[button1]",那么这里表示的是8px
*/
NSArray *constraints2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[btn1(==30)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(btn1)];
/*
跟上面有点不同,@"V:|-20-[btn1(==30)]",其中"V:"中代表这是垂直方向上的约束,"|-20-"这里的
意思就是距离头部为20px,相当于y坐标为20。后面的"[btn1(==30)]",是指定这个button的高度为
30px.y坐标固定了,高度固定了,那这个view的约束就完成了。如果你有需要,你的高度值(或者其他同类
型的)可以使用>=,==,<=来表示,甚至你可以组合来用,像上面的30,你可以指定一个区别,比如:
(>=30,<=40),这同样也是可以的。如果你想表达他的优先级别,可以使用@"V:|-10-
[btn1(==30@1000)]",这个@1000,就是他的级别了。你可以适配XIB或者SB对它的优先级做更多的处理.
*/
[self.view addConstraints:constraints1];
[self.view addConstraints:constraints2];
(2)对于多控件
UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom];
btn2.translatesAutoresizingMaskIntoConstraints = NO;
[btn2 setTitle:@"按钮2" forState:UIControlStateNormal];
[btn2 setTitleColor:[UIColor orangeColor] forState:UIControlStateNormal];
[btn2 setBackgroundColor:[UIColor cyanColor]];
[self.view addSubview:btn2];
NSArray *constraints3 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[btn2]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(btn2)];
NSArray *constraints4 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[btn1]-[btn2(==30)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(btn2,btn1)];
[self.view addConstraints:constraints3];
[self.view addConstraints:constraints4];
我们也可以动态给View加上高度或者宽度,或是其他间距
NSArray *constraints4 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[btn1]-[btn2(==height)]" options:0 metrics:@{@"height":@30} views:NSDictionaryOfVariableBindings(btn2,btn1)];
// 动态去给view加上高度或者宽度,或是其他间距
用VFL让两个View,或是多个View之间等高,等宽。
NSArray *constraints3 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[btn2(btn1)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(btn2,btn1)];
NSArray *constraints4 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[btn1]-[btn2(btn1)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(btn2,btn1)];
// 用VFL让两个View,或是多个View之间等高,等宽。
// 通过@"H:|-[btn2(btn1)]",@"V:[btn1]-[btn2(btn1)]",这两句就可以轻松实现等宽等高了!
效果图
(3)简单练习
- 实现三个按钮居中等分屏幕
- 封装部分代码 使得使用稍加简便
三、格式字符串总结
类型 | 格式 | 示例 |
---|---|---|
水平或垂直放置 | H: V: | V:[view1]-15-[view2] 将View2放到其顶部距离View1底部15点的位置 |
视图 | [item] | [view1] 视图绑定字典将方括号所包围的名称同一个视图实例相匹配 |
父视图 | 丨 | H:丨[view1]丨 使View1的宽度尺寸同父视图的一致 |
关系 | >=,==,<= | H:[view1]-(>=20)-[view2] 使View2的前缘距离View1后缘至少20点 |
度量 | metric | H:[view1(<=someWidth)]V:[view1]-mySpacing-[view2] 度量是键。someWidth和mySpacing必须在传递的度量字典中映射为NSNumber值 |
齐平对齐 | [item][item] | H:[view1][view2] 使View1的后缘同View2的前缘齐平 |
灵活间隔 | [item]-(>=0)-[item] | [view1]-(>=0)-[view2] 根据需要,视图可以伸展分离开,"至少分离0点" |
固定间隔 | [item]-gap-[item] | V:[view1]-20-[view2] 使View1的底部距离View2的顶部20点 |
固定间隔(视图到视图) | [item]-[item] | [view1]-[view2] 在两个视图之间留下一个小的固定空间(8点) |
固定宽度或高度 | [item(size)] [item(==size)] | [view1(50)] 使View1沿着某个坐标轴的范围刚好50点 |
最大和最小宽度/高度 | [item(>=size)] [item(<=size)] | [view1(>=50)][view1(<=50)] 限制View1在某个坐标轴上的最大或者最小尺寸 |
同另一个视图匹配高度/宽度 | [item(>=item)] [item(==item)] [item(<=item)] | [view1(==view2)] 使View1沿着某个坐标轴的尺寸和View2的一致 |
和父视图齐平对齐 | 丨[item] [item]丨 | V:丨[view1] 使View1的顶部和父视图顶部齐平 |
相对于父视图的inset | 丨-[item] [item]-丨 | 丨-[view1] 在某个坐标轴上,父视图和View1之间放置一个固定的间隔(20点) |
优先级(从0到1000) | @value | [view1(<=50@20)] 设置View1在某个坐标轴上的最大尺寸为50点,优先级为一个很低的值(20) |
空间、间隙 | - | [view1]-[view2] |
附练习1代码
// 练习1 实现三个按钮居中等分屏幕
UIButton *demoBtn1 = [self creatBtnWithTitle:@"demoBtn1" andBtnBackgroundColor:[UIColor yellowColor] andTitleColor:[UIColor blackColor]];
[self.view addSubview:demoBtn1];
UIButton *demoBtn2 = [self creatBtnWithTitle:@"demoBtn2" andBtnBackgroundColor:[UIColor blueColor] andTitleColor:[UIColor blackColor]];
[self.view addSubview:demoBtn2];
UIButton *demoBtn3 = [self creatBtnWithTitle:@"demoBtn3" andBtnBackgroundColor:[UIColor purpleColor] andTitleColor:[UIColor blackColor]];
[self.view addSubview:demoBtn3];
NSArray *constraints5 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[btn2]-60-[demoBtn1(==40)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(btn2,demoBtn1)];
NSArray *constraints6 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[btn2]-60-[demoBtn2(==40)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(btn2,demoBtn2)];
NSArray *constraints7 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[btn2]-60-[demoBtn3(==40)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(btn2,demoBtn3)];
NSArray *constraints8 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[demoBtn1]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(demoBtn1)];
NSArray *constraints9 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[demoBtn3]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(demoBtn3)];
NSArray *constraints10 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[demoBtn1][demoBtn2(demoBtn1)][demoBtn3(demoBtn1)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(demoBtn3,demoBtn2,demoBtn1)];
// NSArray *constraints8 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[demoBtn1]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(demoBtn1)];
// NSArray *constraints9 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[demoBtn3]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(demoBtn3)];
// NSArray *constraints10 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[demoBtn1]-5-[demoBtn2(demoBtn1)]-5-[demoBtn3(demoBtn1)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(demoBtn3,demoBtn2,demoBtn1)];
[self.view addConstraints:constraints5];
[self.view addConstraints:constraints6];
[self.view addConstraints:constraints7];
[self.view addConstraints:constraints8];
[self.view addConstraints:constraints9];
[self.view addConstraints:constraints10];
效果图
欢迎关注我们的公众号