简介
本文主要是针对UIScrollView利用Masonry框架来自动布局,因为UIScrollView可以滑动,所以本身布局跟一般的UIView不太一样,然后楼主之前面试的时候也有人问过楼主这个问题,所以楼主想简单总结一下方便你我他它。关于Masonry这个框架,相信大家也都在用它,可以去github上面下载,它里面的各种demo也是非常有用的。
我们设置约束用到的几个方法简单介绍:
一.mas_makeConstraints
:设置约束,如果你的视图不需要根据什么情况更新约束,只需要设置一次的话,请使用它。
错误示范:多次调用
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.scrollView];
[self.scrollView addSubview:self.containerView];
[self.containerView addSubview:self.testButton];
[self.containerView addSubview:self.testView];
// 设置约束
[self setupConstraints];
}
- (void)setupConstraints {
[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view).offset(64);
make.left.right.bottom.equalTo(self.view);
}];
[self.containerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.scrollView);
make.width.equalTo(self.scrollView);
}];
[self.testButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.containerView).offset(100);
make.left.equalTo(self.containerView).offset(50);
make.right.equalTo(self.containerView).offset(-50);
make.height.equalTo(@40);
}];
[self.testView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.testButton.mas_bottom).offset(200);
make.left.right.equalTo(self.containerView);
make.height.equalTo(@400);
make.bottom.equalTo(self.containerView);
}];
}
- (void)testButtonClick {
[self setupConstraints];
}
结果:
第一次添加约束时,约束数组是酱紫的:
这时候约束数组的count为0,是正确的,因为我们之前没有设置过约束,点击测试按钮,再添加一次约束,结果是酱紫的:
这时候,约束数组的count值是4,也是正确的,因为我们添加过一次了,可是这次点击过后,约束数组的count就会变成8了:
很明显,点击一次就会重复累加而不会清除之前的约束,后果可想而知。。。说到这里,大家应该理解mas_makeConstraints
的使用场景了。
二.mas_updateConstraints
:更新约束,如果你的布局需要根据具体情况来更新子控件的约束,那么请使用它。mas_updateConstraints
是对比该对象之前的约束数组,添加过的约束就直接修改它的值,没有添加过的约束就添加上。
结论:无论你点多少次都不重复添加约束,感觉
mas_updateConstraints
真是棒棒哒。
三.mas_remakeConstraints
:在mas_makeConstraints
的基础上会清除掉之前所有的约束,再重新添加新的约束。
UIScrollView的自动布局问题
在利用自动布局来布局UIScrollView时,一般都会在上面添加一个UIView的子控件,来正确布局:
代码片段
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.scrollView];
[self.scrollView addSubview:self.containerView];
[self.containerView addSubview:self.testButton];
[self.containerView addSubview:self.testView];
[self.view setNeedsUpdateConstraints];
}
- (void)updateViewConstraints {
[self.scrollView mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view).offset(64);
make.left.right.bottom.equalTo(self.view);
}];
[self.containerView mas_updateConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.scrollView);
// 确定containerView的宽度
make.width.equalTo(self.scrollView);
}];
[self.testButton mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.containerView).offset(100);
make.left.equalTo(self.containerView).offset(50);
make.right.equalTo(self.containerView).offset(-50);
make.height.equalTo(@40);
}];
[self.testView mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.testButton.mas_bottom).offset(200);
make.left.right.equalTo(self.containerView);
make.height.equalTo(@400);
// 确定containerView的高度
make.bottom.equalTo(self.containerView);
}];
// 必须要调用
[super updateViewConstraints];
}
scrollView的contentSize要根据containerView的高度来设置,containView的高度又要根据它内部的子控件来设置,make.bottom.equalTo(self.containerView)
确定containerView的高度,make.width.equalTo(self.scrollView)
确定containerview的宽度。
模拟复杂环境下UISCrollView的自动布局
现假如有这么一种需求:一个控制器里面有多个视图A B C D,而每个视图的内容要去请求网络获取数据才能确定高度,但是对于这几个网络请求谁先成功获取数据计算到高度是不确定的,如何布局?
代码片段
#pragma mark -- life cycle
- (void)viewDidLoad {
[super viewDidLoad];
self.oneHeight = 0;
self.twoHeight = 0;
self.threeHeight = 0;
self.fourHeight = 0;
[self.view addSubview:self.scrollView];
[self.scrollView addSubview:self.containerView];
[self.containerView addSubview:self.oneLabel];
[self.containerView addSubview:self.twoLabel];
[self.containerView addSubview:self.threeLabel];
[self.containerView addSubview:self.fourLabel];
[self.view setNeedsUpdateConstraints];
// 模拟3s后网络请求数据回来了,oneLabel根据数据获得了高度。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.oneHeight = 80;
[self.view setNeedsUpdateConstraints];
});
// 模拟6s后网络请求数据回来了,fourLabel根据数据获得了高度。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.fourHeight = 100;
[self.view setNeedsUpdateConstraints];
});
// 模拟9s后网络请求数据回来了,twoLabel根据数据获得了高度。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(9 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.twoHeight = 150;
[self.view setNeedsUpdateConstraints];
});
// 模拟12s后网络请求数据回来了,threeLabel根据数据获得了高度。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(12 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.threeHeight = 60;
[self.view setNeedsUpdateConstraints];
});
}
- (void)updateViewConstraints {
[self.scrollView mas_updateConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
[self.containerView mas_updateConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.scrollView);
make.width.equalTo(self.scrollView);
}];
[self.oneLabel mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.containerView).offset(50);
make.left.right.equalTo(self.containerView);
make.height.equalTo(@(self.oneHeight));
}];
[self.twoLabel mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.oneLabel.mas_bottom);
make.left.right.equalTo(self.containerView);
make.height.equalTo(@(self.twoHeight));
}];
[self.threeLabel mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.twoLabel.mas_bottom);
make.left.right.equalTo(self.containerView);
make.height.equalTo(@(self.threeHeight));
}];
[self.fourLabel mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.threeLabel.mas_bottom);
make.left.right.equalTo(self.containerView);
make.height.equalTo(@(self.fourHeight));
make.bottom.equalTo(self.containerView);
}];
[super updateViewConstraints];
}
结果:楼主做的这个gif图跟实际有点差别,将就看