从一开始——我的iOS学习之路

2018-06-13 Masonry的学习

2018-06-13  本文已影响52人  肠粉白粥_Hoben

配置这玩意花了我一个早上_(:з」∠)_,详情请看之前我写的文章。

今天,参考了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和equalTo区别与使用

以下两者等价:

  1. mas_EqualTo(100)

  2. equalTo(@100)

以下两者等价:

  1. make.bottom.mas_equalTo(view.mas_bottom);

  2. 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:

因此,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之后,可以看到效果如下:
上一篇下一篇

猜你喜欢

热点阅读