2018-06-13 Masonry的学习
配置这玩意花了我一个早上_(:з」∠)_,详情请看之前我写的文章。
今天,参考了Masonry介绍与使用实践(快速上手Autolayout),我们来学习一下怎么使用这个Masonry。
先看看流程是怎样的:
初始化subView -> 添加subView ->为subView添加约束(位置、大小等)
1.简单居中
首先来个最简单的:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//初始化subView
UIView *sv = [[UIView alloc] init];
sv.backgroundColor = [UIColor blackColor];
//添加subView
[self.view addSubview: sv];
//为subView添加约束
[sv mas_makeConstraints: ^(MASConstraintMaker *make) {
//居中约束
make.center.equalTo(self.view);
//尺寸约束
make.size.mas_equalTo(CGSizeMake(300, 300));
}];
}
其中mas_equalTo支持的类型,除了NSNumber支持的那些数值类型之外 就只支持CGPoint、CGSize、UIEdgeInsets
而equalTo支持的类型为id
以下两者等价:
-
mas_EqualTo(100)
-
equalTo(@100)
以下两者等价:
-
make.bottom.mas_equalTo(view.mas_bottom);
-
make.bottom.equalTo(view);
// 添加这个宏,就不用带mas_前缀
#define MAS_SHORTHAND
// 添加这个宏,equalTo就等价于mas_equalTo
#define MAS_SHORTHAND_GLOBALS
// 这个头文件一定要放在上面两个宏的后面
#import "Masonry.h"
2.让一个view略小于SuperView
在view已经建立好的基础上再建立一个subView2,这次是基于边缘来定位:
UIView *sv1 = [[UIView alloc] init];
sv1.backgroundColor = [UIColor redColor];
[sv addSubview: sv1];
[sv1 mas_makeConstraints: ^(MASConstraintMaker *make) {
//方法1
make.edges.equalTo(sv).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));
//方法2
make.top.equalTo(sv).with.offset(10);
make.left.equalTo(sv).with.offset(10);
make.bottom.equalTo(sv).with.offset(-10);
make.right.equalTo(sv).with.offset(-10);
//方法3
make.top.left.bottom.and.right.equalTo(sv).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));
}];
在这里,.and和.with其实什么都没做,可加可不加,但是加起来就看上去很舒服。
3.加入两个view并设置大小
这次来基于位置来定位两个view,并引入我们的精髓——autoLayout
int padding1 = 10;
UIView *sv2 = [[UIView alloc] init];
UIView *sv3 = [[UIView alloc] init];
sv2.backgroundColor = [UIColor orangeColor];
sv3.backgroundColor = [UIColor orangeColor];
[self.view addSubview: sv2];
[self.view addSubview: sv3];
[sv2 mas_makeConstraints: ^(MASConstraintMaker *make) {
//令纵向中点相等
make.centerY.equalTo(sv.mas_centerY);
//距离左边10
make.left.equalTo(sv.mas_left).with.offset(padding1);
//两者间距10
make.right.equalTo(sv3.mas_left).with.offset(-padding1);
//设置sv2高度
make.height.equalTo(@150);
//和sv3等宽
make.width.equalTo(sv3);
}];
[sv3 mas_makeConstraints: ^(MASConstraintMaker *make) {
//令纵向中点相等
//以下两者等价
// make.centerY.mas_equalTo(sv.mas_centerY);
make.centerY.equalTo(sv.mas_centerY);
//左边距离sv2的右边10
make.left.equalTo(sv2.mas_right).with.offset(padding1);
//右边距离sv的右边10
//以下两者等价
// make.right.mas_equalTo(sv.mas_right).with.offset(-padding1);
make.right.equalTo(sv.mas_right).with.offset(-padding1);
//设置sv3高度
//以下两者等价
// make.height.mas_equalTo(150);
make.height.equalTo(@150);
//和sv2等宽
//以下两者等价
// make.width.equalTo(sv2);
make.width.equalTo(sv2.mas_width);
}];
看到我们的结果后,很好奇,width明明就没有设置大小,但是还是给我们显示出来了,这就是autoLayout的精髓了,因为我们能通过已知的约束求出sv2和sv3的width:
- sv的width为300
- sv2的left和sv的left距离10
- sv3的right和sv的right距离10
- sv2的right和sv3的left距离10
- sv2和sv3的width相等
因此,width = (300 - 10 - 10 - 10) / 2 = 135
由于已经设置了centerY
4.用UIScrollView包含container展示
这次我们再用UIScrollView来体现autoLayout的优越性:
UIScrollView *scrollView = [[UIScrollView alloc] init];
scrollView.backgroundColor = [UIColor whiteColor];
[sv addSubview: scrollView];
[scrollView mas_makeConstraints: ^(MASConstraintMaker *make) {
make.edges.equalTo(sv).insets(UIEdgeInsetsMake(5, 5, 5, 5));
}];
UIView *container = [[UIView alloc] init];
[scrollView addSubview: container];
UIView *lastView = nil;
[container mas_makeConstraints: ^(MASConstraintMaker *make) {
make.edges.equalTo(scrollView);
//注意这里要设置多width
make.width.equalTo(scrollView);
}];
int count = 10;
for (int i = 1; i <= count; i++) {
UIView *thisView = [[UIView alloc] init];
[container addSubview: thisView];
thisView.backgroundColor = [UIColor colorWithHue:( arc4random() % 256 / 256.0 )
saturation:( arc4random() % 128 / 256.0 ) + 0.5
brightness:( arc4random() % 128 / 256.0 ) + 0.5
alpha:1];
[thisView mas_makeConstraints: ^(MASConstraintMaker *make) {
make.width.equalTo(container);
make.left.and.right.equalTo(container);
make.height.equalTo(@(20 * i));
if (lastView) {
//lastView为上一个view,则接着上一个view
make.top.equalTo(lastView.mas_bottom);
}
else {
make.top.equalTo(container.mas_top);
}
}];
lastView = thisView;
}
[container mas_makeConstraints: ^(MASConstraintMaker *make) {
make.bottom.equalTo(lastView.mas_bottom);
}];
通过自己手敲代码重现一次,有一件事情很重要:就是必须声明该布局的大小(width、height)和位置(left、right、bottom、top或者edges)!
可以看到,这样就可以自动确定子控件的高度了。
5.等间距填充
autoLayout并没有直接提供等间隙排列的方法,因此,此文的作者写了一个Category,思路大概是,获得count+1个等宽高空格,用于填充它们之间的间隙,然后链式调用,利用autoLayout,来进行等间距填充。
- (void) distributeSpacingHorizontallyWith: (NSArray *) views
{
NSMutableArray *spaces = [NSMutableArray arrayWithCapacity: views.count + 1];
//生成(views.count+1)个宽高相等的空格,用于填充views之间的空隙
for (int i = 0; i < views.count + 1; i++) {
UIView *v = [[UIView alloc] init];
[spaces addObject: v];
[self addSubview: v];
[v mas_makeConstraints: ^(MASConstraintMaker *make) {
//width = height
make.width.equalTo(v.mas_height);
}];
}
UIView *v0 = spaces[0];
[v0 mas_makeConstraints: ^(MASConstraintMaker *make) {
make.left.equalTo(self.mas_left);
make.centerY.equalTo(((UIView *) views[0]).mas_centerY);
}];
UIView *lastSpace = v0;
for (int i = 0; i < views.count; i++) {
//从左到右读取view以及space
UIView *obj = views[i];
UIView *space = spaces[i + 1];
[obj mas_makeConstraints: ^(MASConstraintMaker *make) {
make.left.equalTo(lastSpace.mas_right);
}];
[space mas_makeConstraints: ^(MASConstraintMaker *make) {
make.left.equalTo(obj.mas_right);
//记得保持水平相等
make.centerY.equalTo(obj.mas_centerY);
make.width.equalTo(v0);
}];
lastSpace = space;
}
[lastSpace mas_makeConstraints: ^(MASConstraintMaker *make) {
make.right.equalTo(self.mas_right);
}];
}
- (void) distributeSpacingVerticallyWith: (NSArray *) views
{
NSMutableArray *spaces = [NSMutableArray arrayWithCapacity: views.count + 1];
for (int i = 0; i < views.count + 1; i++) {
UIView *v =[[UIView alloc] init];
[spaces addObject: v];
[self addSubview: v];
[v mas_makeConstraints: ^(MASConstraintMaker *make) {
make.width.equalTo(v.mas_height);
}];
}
UIView *v0 = spaces[0];
[v0 mas_makeConstraints: ^(MASConstraintMaker *make) {
make.top.equalTo(self.mas_top);
make.centerX.equalTo(((UIView *)views[0]).mas_centerX);
}];
UIView *lastSpace = v0;
for (int i = 0; i < views.count; i++) {
//从上到下读取view以及space
UIView *obj = views[i];
UIView *space = spaces[i + 1];
[obj mas_makeConstraints: ^(MASConstraintMaker *make) {
make.top.equalTo(lastSpace.mas_bottom);
}];
[space mas_makeConstraints: ^(MASConstraintMaker *make) {
make.top.equalTo(obj.mas_bottom);
make.centerX.equalTo(self.mas_centerX);
make.height.equalTo(v0);
}];
lastSpace = space;
}
[lastSpace mas_makeConstraints: ^(MASConstraintMaker *make) {
make.bottom.equalTo(self.mas_bottom);
}];
}
UIView *fatherView = [[UIView alloc] init];
UIView *verView1 = [[UIView alloc] init];
UIView *verView2 = [[UIView alloc] init];
UIView *horView1 = [[UIView alloc] init];
UIView *horView2 = [[UIView alloc] init];
[sv addSubview: fatherView];
[sv addSubview: verView1];
[sv addSubview: verView2];
[sv addSubview: horView1];
[sv addSubview: horView2];
fatherView.backgroundColor = [UIColor redColor];
verView1.backgroundColor = [UIColor redColor];
verView2.backgroundColor = [UIColor redColor];
horView1.backgroundColor = [UIColor redColor];
horView2.backgroundColor = [UIColor redColor];
[fatherView mas_makeConstraints: ^(MASConstraintMaker *make) {
make.centerX.equalTo(@[verView1, verView2]);
make.centerY.equalTo(@[horView1, horView2]);
make.size.mas_equalTo(CGSizeMake(40, 40));
}];
[verView1 mas_makeConstraints: ^(MASConstraintMaker *make) {
make.size.mas_equalTo(CGSizeMake(50, 20));
}];
[verView2 mas_makeConstraints: ^(MASConstraintMaker *make) {
make.size.mas_equalTo(CGSizeMake(40, 60));
}];
[horView1 mas_makeConstraints: ^(MASConstraintMaker *make) {
make.size.mas_equalTo(CGSizeMake(70, 20));
}];
[horView2 mas_makeConstraints: ^(MASConstraintMaker *make) {
make.size.mas_equalTo(CGSizeMake(50, 50));
}];
[sv distributeSpacingVerticallyWith: @[fatherView, verView1, verView2]];
[sv distributeSpacingHorizontallyWith: @[fatherView, horView1, horView2]];
6.学会debug
在这么复杂的非可视化页面构造中,难免会出现一些constraint的冲突,就会出现以下报错:
这些错误可以通过编译,却会把界面弄得面目全非,但是,这么多的代码怎么找。。
幸好,红框框住的错误信息已经给我们提示了,我们需要加个断点,来定位到哪里出现了错误:
可以看到,红框的内容就是罪魁祸首,这里我不小心把space写成spaces了,多了一个字母,就出bug了,挺烦的= =
经过debug之后,可以看到效果如下: