用过Autolayout
都知道,用代码实现约束是多么麻烦的一件事,虽然有VFL
可视化语言在一定程度上减少了代码量,但是代码看起来有点别扭。后来发现这货:Masonry。
Masonry有很多简便的写法,比如
设置高度
1
2
3
4
5
[self.indicatorView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(self.view);
//全写
make.height.equalTo(self.view.mas_height);
}];
左对其
1
2
3
4
5
[self.indicatorView mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.equalTo(self.view);
//全写
make.height.equalTo(self.view.mas_leading);
}];
向上的约束
1
2
3
4
5
[self.indicatorView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view);
//全写
make.height.equalTo(self.view.mas_top);
}];
x轴中心对其
1
2
3
4
5
[self.indicatorView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view);
//全写
make.height.equalTo(self.mas_centerX);
}];
具体例子
例子中用的都是简写的方式
倍数约束multipliedBy
1
2
3
4
5
6
[self.indicatorView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view);
make.height.equalTo(self.view);
make.top.equalTo(self.view);
make.width.equalTo(self.view).multipliedBy(0.5);
}];
self.indicatorView
宽度是self.view
的0.5倍
对齐
1
2
3
4
5
6
7
8
[self.passwordView mas_updateConstraints:^(MASConstraintMaker *make) {
//左对齐
make.leading.equalTo(self.passwordField);
//右对齐
make.trailing.equalTo(self.passwordField);
make.top.equalTo(self.passwordField.mas_bottom).offset(10);
make.height.equalTo(@10);
}];
passwordView
和passwordField
左右对其
适配iOS 6和iOS 7
1
2
3
4
5
6
7
8
9
10
11
12
[self.passwordTextField mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.equalTo(@20);
make.trailing.equalTo(@-20);
if (IOS7) {
UIView *topLayoutGuide = (id)self.topLayoutGuide;
make.top.equalTo(topLayoutGuide.mas_bottom).with.offset(20);
}
else{
make.top.equalTo(self.view.mas_top).with.offset(20);
}
make.height.equalTo(@36.0f);
}];
self.topLayoutGuide
是UIView
子类,所以可以和这个子类进行相对布局
x轴中心对其
1
2
3
4
5
6
7
8
9
10
11
self.nextButton = [[UIButton alloc] init];
self.nextButton.backgroundColor = [UIColor blackColor];
[self.view addSubview:_nextButton];
[self.nextButton mas_makeConstraints:^(MASConstraintMaker *make) {
//x轴中心对其
make.centerX.equalTo(self.view);
make.top.equalTo(self.mas_bottom).with.offset(200);
make.width.equalTo(@100);
}];
self.nextButton
和其父视图x轴对其
和父视图一样大小
1
2
3
4
UIEdgeInsets ed = UIEdgeInsetsMake(0, 0, 0, 0);
[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view).with.insets(ed);
}];
甚至可以这么用
1
2
3
[self.scrollView makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
UIScrollView布局
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
- (void)viewDidLoad
{
self.scrollView = UIScrollView.new;
self.scrollView.backgroundColor = [UIColor yellowColor];
self.scrollView.pagingEnabled = YES;
[self.view addSubview:_scrollView];
[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
UIView *view1 = [[UIView alloc] init];
view1.backgroundColor = [UIColor grayColor];
[self.scrollView addSubview:view1];
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(@0);
make.bottom.equalTo(@0);
make.width.equalTo(@(self.view.frame.size.width));
make.top.equalTo(@0);
}];
UIView *view2 = [[UIView alloc] init];
view2.backgroundColor = [UIColor cyanColor];
[self.scrollView addSubview:view2];
[view2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(view1.mas_right);
make.bottom.equalTo(@0);
make.width.equalTo(@(self.view.frame.size.width));
make.top.equalTo(@0);
make.right.equalTo(@0);
}];
}
顺便说一下对于UIScrollView应该怎么布局:上面的代码中相比view1
的布局,view2
多了一个对视图右侧的约束,对于一般的布局来说,这个约束其实是多余,因为前四个约束已经明确了view2
的frame
。但是在UIScrollView
中必须考虑contentSize
,也就是说内部的子视图的加入必须确定UIScrollView
的滚动范围,想象一下如果没有这个约束,把这两个视图加入中UIScrollView
中,对于UIScrollView
其实可以随意把contentSize
调到self.view.frame.size.width * 2
以上的任意宽度,这样也可以容纳这两个视图,所以说这样还不至于约束整个UIScrollView
。
那么在约束UIScrollView
的时候,如果希望横向滚动,就必须在UIScrollView
的左右侧有约束。同样的,对于竖向滚动就必须在UIScrollView
的上下侧有约束。
跟踪约束
1
@property (nonatomic, strong) MASConstraint *top;
1
2
3
4
5
6
[self.button mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.equalTo(@0);
make.trailing.equalTo(@0);
self.top = make.top.equalTo(@80);
make.height.equalTo(@50);
}];
修改约束,执行动画
1
2
3
4
self.top.mas_equalTo(@100);
[UIView animateWithDuration:0.3 delay:0.0f options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.view.superview layoutIfNeeded];
}completion:NULL];
取消跟踪
1
[self.top uninstall];
快速设置宽高
1
2
3
[self.topView mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(self.view.frame.size);
}];
甚至可以
1
2
3
[self.topView mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(self.view);
}];
混合
1
2
3
4
5
[self.view.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *obj, NSUInteger idx, BOOL *stop) {
if (obj.firstItem == _otherView && obj.secondItem == _IDNumberFiled && obj.constant == 29.0f) {
[self.view removeConstraint:obj];
}
}];
删除IB中的约束,添加MASConstraint约束
1
2
3
[self.otherView mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(_IDNumberFiled.mas_bottom).offset(60.0f);
}];
这样就比较容易更新约束并实现动画
1
2
3
4
5
6
[self.otherView mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(_IDNumberFiled.mas_bottom).offset(100.0f);
}];
[UIView animateWithDuration:0.3 delay:0.0f options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.view layoutIfNeeded];
}completion:NULL];
最后
关于NSLayoutAttributeLeading
NSLayoutAttributeTrailing
和NSLayoutAttributeLeft
NSLayoutAttributeRight
的区别:leading/trailing在某些从右至左习惯的地区(希伯来语等)会变成, leading是右边,trailing是左边,而left/right永远代表的是左右侧。还有,看下面两种写法
1
2
3
4
5
6
7
[self.otherView mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.contentView);
}];
// 或
[self.otherView mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(@0.0f);
}];
个人建议使用第二种,因为第一种写法很容易写错父视图,比如otherView是self.view.contentView
的子视图,但是可有可能就写成self.view
,这时候 Masonry 又是不会给出任何警告。