Masonry 使用

打开Masonry的源代码,可以发现两个属性

/*

/**

 * Sets the NSLayoutConstraint multiplier property

 */

- (MASConstraint * (^)(CGFloat multiplier))multipliedBy;

/**

 * Sets the NSLayoutConstraint multiplier to 1.0/dividedBy

 */

- (MASConstraint * (^)(CGFloat divider))dividedBy;


*/

multipler属性表示约束值为约束对象的乘因数, dividedBy属性表示约束值为约束对象的除因数,可用于设置view的宽高比

这两个属性可以设置视图中的宽高比例

 上代码。。

    [aview makeConstraints:^(MASConstraintMaker *make) {

        make.left.mas_equalTo(self.view);

        make.bottom.mas_equalTo(self.view);

        make.right.mas_equalTo(WeSelf.InputYuYinbtn.left);

        make.height.mas_equalTo(aview.width).multipliedBy(0.6);// 高/宽 == 0.6

    }];

简要

自动布局最重要的是约束:UI元素间关系的数学表达式。约束包括尺寸、由优先级和阈值管理的相对位置。它们是添加剂,可能导致约束冲突 、约束不足造成布局无法确定 。这两种情况都会产生异常。

使用前:AutoLayout关于更新的几个方法的区别

  • setNeedsLayout:告知页面需要更新,但是不会立刻开始更新。执行后会立刻调用layoutSubviews。
  • layoutIfNeeded:告知页面布局立刻更新。所以一般都会和setNeedsLayout一起使用。如果希望立刻生成新的frame需要调用此方法,利用这点一般布局动画可以在更新布局后直接使用这个方法让动画生效。
  • layoutSubviews:系统重写布局
  • setNeedsUpdateConstraints:告知需要更新约束,但是不会立刻开始
  • updateConstraintsIfNeeded:告知立刻更新约束
  • updateConstraints:系统更新约束

使用

1. 基本使用

  • mas_makeConstraints:添加约束
  • mas_updateConstraints:更新约束、亦可添加新约束
  • mas_remakeConstraints:重置之前的约束

  • multipler属性表示约束值为约束对象的乘因数, dividedBy属性表示约束值为约束对象的除因数,可用于设置view的宽高比

    // 进行屏幕的适配的时候,往往需要根据屏幕宽度来适配一个相应的高度,在此推荐使用如下约束的方式来进行控件的适配
    [self.topView addSubview:self.topInnerView];
    [self.topInnerView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.height.equalTo(self.topView.mas_height).dividedBy(3);
        make.width.and.height.lessThanOrEqualTo(self.topView);
        make.width.and.height.equalTo(self.topView).with.priorityLow();
        make.center.equalTo(self.topView);
    }];
  • priorityLow()设置约束优先级
  • #define MAS_SHORTHAND_GLOBALS使用全局宏定义,可以使equalTo等效于mas_equalTo
  • #define MAS_SHORTHAND使用全局宏定义, 可以在调用masonry方法的时候不使用mas_前缀
// 这里注意到一个地方,就是当使用了这个全局宏定义之后,发现可以有个类`NSArray+MASAdditions.h`,看了之后发现可以
self.buttonViews = @[ raiseButton, lowerButton, centerButton ];
// 之后可以在updateConstraints 方法中
- (void)updateConstraints {
   [self.buttonViews updateConstraints:^(MASConstraintMaker *make) {
        make.baseline.equalTo(self.mas_centerY).with.offset(self.offset);
    }];
    [super updateConstraints];  
}
  • 动态修改视图约束:
    // 创建视图约束
    [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
          self.animatableConstraint = make.edges.equalTo(superview).insets(paddingInsets).priorityLow();
    ]];
    // 更改约束 (另一处方法中)
    UIEdgeInsets paddingInsets = UIEdgeInsetsMake(padding, padding, padding, padding);
    self.animatableConstraint.insets = paddingInsets;
    [self layoutIfNeeded];
  • debug模式:
    // 对某个view添加key值
    greenView.mas_key = @"greenView";
    // 或者如下顺序
    MASAttachKeys(greenView, redView, blueView, superview);
    // 同样的对每条约束亦可以添加key
    make.height.greaterThanOrEqualTo(@5000).key(@"ConstantConstraint");
  • preferredMaxLayoutWidth: 多行label的约束问题
// 已经确认好了位置
// 在layoutSubviews中确认label的preferredMaxLayoutWidth值
- (void)layoutSubviews {
    [super layoutSubviews];
    // 你必须在 [super layoutSubviews] 调用之后,longLabel的frame有值之后设置preferredMaxLayoutWidth
    self.longLabel.preferredMaxLayoutWidth = self.frame.size.width-100;
    // 设置preferredLayoutWidth后,需要重新布局
    [super layoutSubviews];
}
  • scrollView使用约束的问题:原理通过一个contentView来约束scrollView的contentSize大小,也就是说以子控件的约束条件,来控制父视图的大小
// 1. 控制scrollView大小(显示区域)
[self.scrollView makeConstraints:^(MASConstraintMaker *make) {
     make.edges.equalTo(self.view);
}];
// 2. 添加一个contentView到scrollView,并且添加好约束条件
[contentView makeConstraints:^(MASConstraintMaker *make) {
     make.edges.equalTo(self.scrollView);
     // 注意到此处的宽度约束条件,这个宽度的约束条件是比添加项
     make.width.equalTo(self.scrollView);
}];
// 3. 对contentView的子控件做好约束,达到可以控制contentView的大小
  • 新方法:2个或2个以上的控件等间隔排序
/**
 *  多个控件固定间隔的等间隔排列,变化的是控件的长度或者宽度值
 *
 *  @param axisType        轴线方向
 *  @param fixedSpacing    间隔大小
 *  @param leadSpacing     头部间隔
 *  @param tailSpacing     尾部间隔
 */
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType 
                    withFixedSpacing:(CGFloat)fixedSpacing l
                          eadSpacing:(CGFloat)leadSpacing 
                         tailSpacing:(CGFloat)tailSpacing;

/**
 *  多个固定大小的控件的等间隔排列,变化的是间隔的空隙
 *
 *  @param axisType        轴线方向
 *  @param fixedItemLength 每个控件的固定长度或者宽度值
 *  @param leadSpacing     头部间隔
 *  @param tailSpacing     尾部间隔
 */
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType 
                 withFixedItemLength:(CGFloat)fixedItemLength 
                         leadSpacing:(CGFloat)leadSpacing 
                         tailSpacing:(CGFloat)tailSpacing;

使用方法很简单,因为它是NSArray的类扩展:

//  创建水平排列图标 arr中放置了2个或连个以上的初始化后的控件
//  alongAxis 轴线方向   固定间隔     头部间隔      尾部间隔
[arr mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:20 leadSpacing:5 tailSpacing:5];
[arr makeConstraints:^(MASConstraintMaker *make) {
       make.top.equalTo(@60);
       make.height.equalTo(@60);
}];

2. 注意事项

  • 约束视图对象只有在被addSubview之后,才能给视图添加约束
  • 当你的所有约束都在 updateConstraints 内调用的时候,你就需要在此调用此方法,因为 updateConstraints方法是需要触发的
// 调用在view 内部,而不是viewcontroller
+ (BOOL)requiresConstraintBasedLayout {
    return YES;
}

/**
 *  苹果推荐 约束 增加和修改 放在此方法种
 */
- (void)updateConstraints {
    [self.growingButton updateConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self);
        make.width.equalTo(@(self.buttonSize.width)).priorityLow();
        make.height.equalTo(@(self.buttonSize.height)).priorityLow();
        make.width.lessThanOrEqualTo(self);
        make.height.lessThanOrEqualTo(self);
    }];
    //最后记得回调super方法
    [super updateConstraints];
}
  • 如果想要约束变换之后实现动画效果,则需要执行如下操作
    // 通知需要更新约束,但是不立即执行
    [self setNeedsUpdateConstraints];
    // 立即更新约束,以执行动态变换
    // update constraints now so we can animate the change
    [self updateConstraintsIfNeeded];
    // 执行动画效果, 设置动画时间
    [UIView animateWithDuration:0.4 animations:^{
       [self layoutIfNeeded];
    }];



文/码码乐趣(简书作者)
原文链接:http://www.jianshu.com/p/1d1a1165bb04
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
1
MagicNumber -> autoresizingMask -> autolayout

以上是纯手写代码所经历的关于页面布局的三个时期

在iphone1-iphone3gs时代 window的size固定为(320,480) 我们只需要简单计算一下相对位置就好了

在iphone4-iphone4s时代 苹果推出了retina屏 但是给了码农们非常大的福利:window的size不变

在iphone5-iphone5s时代 window的size变了(320,568) 这时autoresizingMask派上了用场(为啥这时候不用Autolayout? 因为还要支持ios5呗) 简单的适配一下即可

在iphone6+时代 window的width也发生了变化(相对5和5s的屏幕比例没有变化) 终于是时候抛弃autoresizingMask改用autolayout了(不用支持ios5了 相对于屏幕适配的多样性来说autoresizingMask也已经过时了)

那如何快速的上手autolayout呢? 说实话 当年ios6推出的同时新增了autolayout的特性 我看了一下官方文档和demo 就立马抛弃到一边了 因为实在过于的繁琐和啰嗦(有过经验的朋友肯定有同感)

直到iPhone6发布之后 我知道使用autolayout势在必行了 这时想起了以前在浏览Github看到过的一个第三方库Masonry 在花了几个小时的研究使用后 我就将autolayout掌握了(重点是我并没有学习任何的官方文档或者其他的关于autolayout的知识) 这就是我为什么要写下这篇文章来推荐它的原因.

介绍

Masonry 源码:https://github.com/Masonry/Masonry

Masonry是一个轻量级的布局框架 拥有自己的描述语法 采用更优雅的链式语法封装自动布局 简洁明了 并具有高可读性 而且同时支持 iOS 和 Max OS X。

我们先来看一段官方的sample code来认识一下Masonry

1
2
3
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(superview).with.insets(padding);
}];

看到block里面的那句话: make edges equalTo superview with insets

通过链式的自然语言 就把view1给autolayout好了 是不是简单易懂?

使用

看一下Masonry支持哪一些属性

1
2
3
4
5
6
7
8
9
10
11
@property (nonatomic, strong, readonly) MASConstraint *left;
@property (nonatomic, strong, readonly) MASConstraint *top;
@property (nonatomic, strong, readonly) MASConstraint *right;
@property (nonatomic, strong, readonly) MASConstraint *bottom;
@property (nonatomic, strong, readonly) MASConstraint *leading;
@property (nonatomic, strong, readonly) MASConstraint *trailing;
@property (nonatomic, strong, readonly) MASConstraint *width;
@property (nonatomic, strong, readonly) MASConstraint *height;
@property (nonatomic, strong, readonly) MASConstraint *centerX;
@property (nonatomic, strong, readonly) MASConstraint *centerY;
@property (nonatomic, strong, readonly) MASConstraint *baseline;

这些属性与NSLayoutAttrubute的对照表如下

43.jpg

其中leading与left trailing与right 在正常情况下是等价的 但是当一些布局是从右至左时(比如阿拉伯文?没有类似的经验) 则会对调 换句话说就是基本可以不理不用 用left和right就好了

在ios8发布后 又新增了一堆奇奇怪怪的属性(有兴趣的朋友可以去瞅瞅) Masonry暂时还不支持(不过你要支持ios6,ios7 就没必要去管那么多了)

在讲实例之前 先介绍一个MACRO

1
#define WS(weakSelf)  __weak __typeof(&*self)weakSelf = self;

快速的定义一个weakSelf 当然是用于block里面啦 下面进入正题(为了方便 我们测试的superView都是一个size为(300,300)的UIView)

下面 通过一些简单的实例来简单介绍如何轻松愉快的使用Masonry:

1. [基础] 居中显示一个view

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
     
    WS(ws);
     
    UIView *sv = [UIView new];
    [sv showPlaceHolder];
    sv.backgroundColor = [UIColor blackColor];
    [self.view addSubview:sv];
    [sv mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(ws.view);
        make.size.mas_equalTo(CGSizeMake(300, 300));
    }];
     
}

代码效果

04.PNG

使用我之间写的MMPlaceHolder 可以看到superview已经按照我们预期居中并且设置成了适当的大小

那么先看看这几行代码

1
2
3
4
5
6
7
8
9
10
11
12
//从此以后基本可以抛弃CGRectMake了
UIView *sv = [UIView new];
//在做autoLayout之前 一定要先将view添加到superview上 否则会报错
[self.view addSubview:sv];
//mas_makeConstraints就是Masonry的autolayout添加函数 将所需的约束添加到block中行了
[sv mas_makeConstraints:^(MASConstraintMaker *make) {
//将sv居中(很容易理解吧?)
    make.center.equalTo(ws.view);
     
    //将size设置成(300,300)
    make.size.mas_equalTo(CGSizeMake(300, 300));
}];

这里有两个问题要分解一下

首先在Masonry中能够添加autolayout约束有三个函数

1
2
3
4
5
6
7
8
9
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block;
- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block;
- (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block;
/*
mas_makeConstraints 只负责新增约束 Autolayout不能同时存在两条针对于同一对象的约束 否则会报错 
mas_updateConstraints 针对上面的情况 会更新在block中出现的约束 不会导致出现两个相同约束的情况
mas_remakeConstraints 则会清除之前的所有约束 仅保留最新的约束
三种函数善加利用 就可以应对各种情况了
*/

其次 equalTo 和 mas_equalTo的区别在哪里呢? 其实 mas_equalTo是一个MACRO

1
2
3
4
#define mas_equalTo(...)                 equalTo(MASBoxValue((__VA_ARGS__)))
#define mas_greaterThanOrEqualTo(...)    greaterThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_lessThanOrEqualTo(...)       lessThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_offset(...)                  valueOffset(MASBoxValue((__VA_ARGS__)))

可以看到 mas_equalTo只是对其参数进行了一个BOX操作(装箱) MASBoxValue的定义具体可以看看源代码 太长就不贴出来了

所支持的类型 除了NSNumber支持的那些数值类型之外 就只支持CGPoint CGSize UIEdgeInsets

介绍完这几个问题 我们就继续往下了 PS:刚才定义的sv会成为我们接下来所有sample的superView

2. [初级] 让一个view略小于其superView(边距为10)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
UIView *sv1 = [UIView new];
[sv1 showPlaceHolder];
sv1.backgroundColor = [UIColor redColor];
[sv addSubview:sv1];
[sv1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(sv).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));
     
    /* 等价于
    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);
    */
     
    /* 也等价于
    make.top.left.bottom.and.right.equalTo(sv).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));
    */
}];

代码效果

05.PNG

可以看到 edges 其实就是top,left,bottom,right的一个简化 分开写也可以 一句话更省事

那么为什么bottom和right里的offset是负数呢? 因为这里计算的是绝对的数值 计算的bottom需要小鱼sv的底部高度 所以要-10 同理用于right

这里有意思的地方是and和with 其实这两个函数什么事情都没做

1
2
3
4
5
6
- (MASConstraint *)with {
    return self;
}
- (MASConstraint *)and {
    return self;
}

但是用在这种链式语法中 就非常的巧妙和易懂 不得不佩服作者的心思(虽然我现在基本都会省略)

3. [初级] 让两个高度为150的view垂直居中且等宽且等间隔排列 间隔为10(自动计算其宽度)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int padding1 = 10;
[sv2 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.centerY.mas_equalTo(sv.mas_centerY);
    make.left.equalTo(sv.mas_left).with.offset(padding1);
    make.right.equalTo(sv3.mas_left).with.offset(-padding1);
    make.height.mas_equalTo(@150);
    make.width.equalTo(sv3);
}];
[sv3 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.centerY.mas_equalTo(sv.mas_centerY);
    make.left.equalTo(sv2.mas_right).with.offset(padding1);
    make.right.equalTo(sv.mas_right).with.offset(-padding1);
    make.height.mas_equalTo(@150);
    make.width.equalTo(sv2);
}];

代码效果

06.PNG

这里我们在两个子view之间互相设置的约束 可以看到他们的宽度在约束下自动的被计算出来了

4. [中级] 在UIScrollView顺序排列一些view并自动计算contentSize

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
34
35
36
37
38
39
40
41
42
UIScrollView *scrollView = [UIScrollView new];
scrollView.backgroundColor = [UIColor whiteColor];
[sv addSubview:scrollView];
[scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(sv).with.insets(UIEdgeInsetsMake(5,5,5,5));
}];
UIView *container = [UIView new];
[scrollView addSubview:container];
[container mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(scrollView);
    make.width.equalTo(scrollView);
}];
int count = 10;
UIView *lastView = nil;
for ( int i = 1 ; i <= count ; ++i )
{
    UIView *subv = [UIView new];
    [container addSubview:subv];
    subv.backgroundColor = [UIColor colorWithHue:( arc4random() % 256 / 256.0 )
                                      saturation:( arc4random() % 128 / 256.0 ) + 0.5
                                      brightness:( arc4random() % 128 / 256.0 ) + 0.5
                                           alpha:1];
     
    [subv mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.and.right.equalTo(container);
        make.height.mas_equalTo(@(20*i));
         
        if ( lastView )
        {
            make.top.mas_equalTo(lastView.mas_bottom);
        }
        else
        {
            make.top.mas_equalTo(container.mas_top);
        }
    }];
     
    lastView = subv;
}
[container mas_makeConstraints:^(MASConstraintMaker *make) {
    make.bottom.equalTo(lastView.mas_bottom);
}];

头部效果

07.PNG

尾部效果

08.PNG

从scrollView的scrollIndicator可以看出 scrollView的内部已如我们所想排列好了

这里的关键就在于container这个view起到了一个中间层的作用 能够自动的计算uiscrollView的contentSize

5. [高级] 横向或者纵向等间隙的排列一组view

很遗憾 autoLayout并没有直接提供等间隙排列的方法(Masonry的官方demo中也没有对应的案例) 但是参考案例3 我们可以通过一个小技巧来实现这个目的 为此我写了一个Category

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
@implementation UIView(Masonry_LJC)
- (void) distributeSpacingHorizontallyWith:(NSArray*)views
{
    NSMutableArray *spaces = [NSMutableArray arrayWithCapacity:views.count+1];
     
    for ( int i = 0 ; i < views.count+1 ; ++i )
    {
        UIView *v = [UIView new];
        [spaces addObject:v];
        [self addSubview:v];
         
        [v mas_makeConstraints:^(MASConstraintMaker *make) {
            make.width.equalTo(v.mas_height);
        }];
    }    
     
    UIView *v0 = spaces[0];
     
    __weak __typeof(&*self)ws = self;
     
    [v0 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(ws.mas_left);
        make.centerY.equalTo(((UIView*)views[0]).mas_centerY);
    }];
     
    UIView *lastSpace = v0;
    for ( int i = 0 ; i < views.count; ++i )
    {
        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(ws.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 new];
        [spaces addObject:v];
        [self addSubview:v];
         
        [v mas_makeConstraints:^(MASConstraintMaker *make) {
            make.width.equalTo(v.mas_height);
        }];
    }
     
     
    UIView *v0 = spaces[0];
     
    __weak __typeof(&*self)ws = self;
     
    [v0 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(ws.mas_top);
        make.centerX.equalTo(((UIView*)views[0]).mas_centerX);
    }];
     
    UIView *lastSpace = v0;
    for ( int i = 0 ; i < views.count; ++i )
    {
        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(obj.mas_centerX);
            make.height.equalTo(v0);
        }];
         
        lastSpace = space;
    }
     
    [lastSpace mas_makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.equalTo(ws.mas_bottom);
    }];
}
@end

简单的来测试一下

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
34
35
36
37
UIView *sv11 = [UIView new];
UIView *sv12 = [UIView new];
UIView *sv13 = [UIView new];
UIView *sv21 = [UIView new];
UIView *sv31 = [UIView new];
sv11.backgroundColor = [UIColor redColor];
sv12.backgroundColor = [UIColor redColor];
sv13.backgroundColor = [UIColor redColor];
sv21.backgroundColor = [UIColor redColor];
sv31.backgroundColor = [UIColor redColor];
[sv addSubview:sv11];
[sv addSubview:sv12];
[sv addSubview:sv13];
[sv addSubview:sv21];
[sv addSubview:sv31];
//给予不同的大小 测试效果
[sv11 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.centerY.equalTo(@[sv12,sv13]);
    make.centerX.equalTo(@[sv21,sv31]);
    make.size.mas_equalTo(CGSizeMake(40, 40));
}];
[sv12 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.size.mas_equalTo(CGSizeMake(70, 20));
}];
[sv13 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.size.mas_equalTo(CGSizeMake(50, 50));
}];
[sv21 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.size.mas_equalTo(CGSizeMake(50, 20));
}];
[sv31 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.size.mas_equalTo(CGSizeMake(40, 60));
}];
[sv distributeSpacingHorizontallyWith:@[sv11,sv12,sv13]];
[sv distributeSpacingVerticallyWith:@[sv11,sv21,sv31]];
[sv showPlaceHolderWithAllSubviews];
[sv hidePlaceHolder];

代码效果

09.PNG

perfect! 简洁明了的达到了我们所要的效果

这里所用的技巧就是 使用空白的占位view来填充我们目标view的旁边 这点通过图上的空白标注可以看出来

小结

通过以上5个案例 我觉得已经把Masonry的常用功能介绍得差不多了 如果你觉得意犹未尽呢 请下载官方的demo来学习

总而言之 Masonry是一个非常优秀的autolayout库 能够节省大量的开发和学习时间 尤其适合我这种纯代码的iOSer 在iPhone6发布后引发的适配潮中 Masonry一定可以助你一臂之力 :)

iOS Autolayout之Masonry解读

字数2609 阅读19472 评论58 

Masonry

  • Masonry是公认非常简洁优美的一款Autolayout框架
  • 我推荐大家重点学习这个框架
  • 我会把Autolayout的思维融合这个框架一起讲解

    Masonry的使用

  • 想要使用第三方Masonry要么要去GitHub上下载原代码下来拖进项目中,要么就直接使用cocoapods,pod search Masonry,下载最新版
  • cocoapods的使用不是本文讨论的范围,大家可以百度一下哈,安装非常简单

    Masonry的讲解

  • 因为之前的一篇关于VFL的文章我自我感觉写的不是太好,其实主要原因是VFL的约束创建非常宏观,如果既要照顾语法讲解,又要照顾约束理解,反而介绍会使文章增加很多口水话,长而乏味
  • Masonry的约束添加思维其实与苹果原API的添加思维是相同的,只是Masonry语法更简洁,代码更优美
  • 在这里,为了融合Autolayout的思想,我依然要说下面几点
    • Autolayout所倡导的两个核心词是约束,参照
    • 而我认为,Autolayout其实核心思想还是为了设置frame
    • 无论我们如何添加约束,最终还是为了确定其位置尺寸
    • 所以,Autolayout的关键就是如何设置约束,让空间满足位置,尺寸这两个必要条件
    • 还有就是,当一个控件的约束已经能够满足上述两个条件了,就不要再添加多余的约束了,很容易会照成约束冲突
    • 除非你想设置其他优先级的约束
    • 优先级会在例子中说明其用处

      例子讲解

  • 由于Masonry是重点推荐的,我会分别用三个例子来讲诉它
    第一个例子
  • 这个例子很简单,仅仅是为了向大家介绍Masonry的语法
  • 比起文字阐述需求,我不如直接上图让大家看得更直接明白
  • 实现后的效果是这样的

图1
  • ok,接下来看看怎么用代码去实现这个效果
  • 先添加四个View

      UIView *redView = [[UIView alloc]init];
      redView.backgroundColor = [UIColor redColor];
      [self.view addSubview:redView];
    
      UIView *blueView = [[UIView alloc]init];
      blueView.backgroundColor = [UIColor blueColor];
      [self.view addSubview:blueView];
    
      UIView *yellow = [[UIView alloc]init];
      yellow.backgroundColor = [UIColor yellowColor];
      [self.view addSubview:yellow];
    
      UIView *green = [[UIView alloc]init];
      green.backgroundColor = [UIColor greenColor];
      [self.view addSubview:green];
  • 创建添加红色View的约束
    • 先来看看语法
    • Masonry的语法可读性非常强
    • 所以我不会在这里死板的一个单词一个单词的介绍
    • 大家在写的时候完全就可以像写句子一样,并且Masonry添加约束都是mas_makeConstraints这个方法
    • 只需要在块中写上想好的约束
    • 比如下面的第一个约束
    • 翻译过来就是使左边等于self.view的左边,间距为0
    • 而在块中主语就是调用者,这里也就是redView
    • 所以使用Masonry,你就想着是用英语在造句就行了,哈哈
    • 还有一点,andwith其实就是get调用者本身,里面仅仅是return self
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.view.mas_left).offset(0);//使左边等于self.view的左边,间距为0
        make.top.equalTo(self.view.mas_top).offset(0);//使顶部与self.view的间距为0
        make.width.equalTo(self.view.mas_width).multipliedBy(0.5);//设置宽度为self.view的一半,multipliedBy是倍数的意思,也就是,使宽度等于self.view宽度的0.5倍
        make.height.equalTo(self.view.mas_height).multipliedBy(0.5);//设置高度为self.view高度的一半

    }];
  • 上面我已经添加了redView的约束,它已经拥有了宽和高,还有水平方向和垂直方向的位置,也就是frame中的x,y,width,height,都有了
  • 所以redView的约束就添加完成了,无需再添加过多的约束
  • 其他的view将要以它为锚点,来添加约束,确定自身的位置尺寸
  • 接下来设置blueView的约束
  • 大家在看下面代码之前可以自己思考,按照图1中的blueView的效果,我们应当怎样添加约束呢?
  • 很明显,只需要与红色等宽高,并且与红色左间距为0,顶部对齐,就可以了
[blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.width.and.height.equalTo(redView);//使宽高等于redView
        make.top.equalTo(redView.mas_top);//与redView顶部对齐
        make.leading.equalTo(redView.mas_right);//与redView的间距为0
    }];
  • 剩下的两个View的约束我就不过多的阐述了,大家可以自己先想一下怎么添加约束,再来看我代码是怎么实现的
  • 当然,很可能你的想法和我的实现不一样,这是很正常的,约束的实现方法有太多了
  • 但是万变不离其宗!一定得保证最终设置完毕后,所有的控件都拥有了位置尺寸
[yellow mas_makeConstraints:^(MASConstraintMaker *make) {
        make.leading.equalTo(redView);//与redView左对齐
        make.top.equalTo(redView.mas_bottom);//与redView底部间距为0
        make.width.and.height.equalTo(redView);//与redView宽高相等
    }];
    [green mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(yellow.mas_right);//与yellow右边间距为0
        make.top.equalTo(blueView.mas_bottom);//与blueView底部间距为0
        make.width.and.height.equalTo(redView);//与redView等宽高
    }];
第二个例子
  • 这个例子我希望大家能对约束的理念有个更深的理解
  • 因为我一路写下来,一直用的都是固定尺寸的例子,或者是固定位置的例子,我怕误导大家认为Autolayout是非常死板的,必须把每个控件的约束添加到满足位置尺寸,再去添加其他控件的约束,这样才不会出错
  • 其实不是这样的,的确,在所有控件添加完约束后,得拥有自己的位置尺寸,但是有时这两个必须条件可以利用相对来满足
  • 接下来我就用例子来解释吧
  • 先让大家看一下效果图

竖屏

横屏
  • 正如大家在图片所看到的,我希望两个等宽高的红色方块能够在屏幕旋转的时候,间距等比例缩放,它们的相对位置是固定的,绝对位置随着屏幕的宽改变而改变
  • 其他三个灰色的方块,它们的宽是不确定,这就是我想要和大家说的相对概念
  • 我并没有固定死灰色方块的宽度,只要求它们与红色方块的间距为0,并且灰色方块的宽度相等
  • 但是红色方块的宽度是固定的,灰色方块就会互相相等的宽度,填充着红色方块间的空隙
  • 接下来看看代码是怎么实现的吧
  • 添加View的代码我就不上了,直接看添加约束的代码
//代码中View的顺序与图中从左到右的View的顺序一致
//例子中,唯一不确定的就是灰色View的宽度,我们先把确定的约束给一个一个的添加上来

//灰1左间距、高度、垂直位置(因为和红1底部对齐)是确定的,添加约束
[gray1 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.height.mas_equalTo(20);
        make.leading.equalTo(self.view.mas_leading).offset(0);
        make.bottom.equalTo(red1.mas_bottom);
    }];

//红1,宽高、左间距、底间距是确定的,添加约束
    [red1 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.width.mas_equalTo(100);
        make.height.mas_equalTo(50);
        make.left.equalTo(gray1.mas_right);
        make.bottom.equalTo(self.view.mas_bottom).offset(-50);
    }];
//灰2,左间距、高度、垂直位置是确定的,宽度要与灰1一致,是为了能均匀填充,添加约束
    [gray2 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.height.and.width.equalTo(gray1);
        make.leading.equalTo(red1.mas_right);
        make.bottom.equalTo(red1.mas_bottom);
    }];
//红2,宽高、左间距、底间距是确定的,添加约束
    [red2 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.height.and.width.equalTo(red1);
        make.leading.equalTo(gray2.mas_right);
        make.bottom.equalTo(red1.mas_bottom);
    }];
//灰3,左间距、右间距、高度、垂直位置是确定的,添加约束
    [gray3 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.height.and.width.equalTo(gray1);
        make.leading.equalTo(red2.mas_right);
        make.trailing.equalTo(self.view.mas_right);
        make.bottom.equalTo(red1.mas_bottom);
    }];
  • 大家看了上面的讲解后,会发现三个灰色方块都没有设置固定的宽
  • 但是它们三个都等宽,红色方块又是固定的,那么在这5个View间距都为0的情况下,灰色方块不就会去挤压红色方块,直到灰色方块宽度相等,那么红色方块也处在了应有的位置么
  • 这就是我想说的相对,红色方块宽度是固定的,那么水平方向上的间距就需要剩下的三个灰色方块去填充,当界面横屏时,三个灰色方块为了相对自身宽度要相同,相对红色边界,self.view边界,间距保持为0,那么就得牺牲自身宽度的稳定,去维持这些相对的约束
  • 希望我这些话能帮助大家更深刻的理解约束,更多的东西需要大家去做项目慢慢体会
第三个例子
  • 最后这个例子是老例子了,我想给大家看看其实Masonry做动画也和其他的Autolayout方法一样,但是添加约束的代码却非常的少,可以和我之前的另一篇文章比较一下
  • 里面的约束我就不讲解了,看了上面的代码,下面的约束对你来说肯定是小菜一碟
  • 约束代码
 UIView *redView = [[UIView alloc]init];
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];
    UIView *greenView = [[UIView alloc]init];
    greenView.backgroundColor = [UIColor greenColor];
    [self.view addSubview:greenView];
    UIView *blueView = [[UIView alloc]init];
    blueView.backgroundColor  = [UIColor blueColor];
    [self.view addSubview:blueView];

    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.view.mas_left).offset(20);
        make.bottom.equalTo(self.view.mas_bottom).offset(-20);
        make.width.equalTo(self.view.mas_width).multipliedBy(0.2);
        make.height.equalTo(self.view.mas_height).multipliedBy(0.2);
    }];
    [greenView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(redView.mas_right).offset(20);
        make.bottom.equalTo(self.view.mas_bottom).offset(-20);
        make.width.equalTo(self.view.mas_width).multipliedBy(0.2);
        make.height.equalTo(self.view.mas_height).multipliedBy(0.2);
    }];
    [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(greenView.mas_right).offset(20);
        make.bottom.equalTo(self.view.mas_bottom).offset(-20);
        make.width.equalTo(self.view.mas_width).multipliedBy(0.2);
        make.height.equalTo(self.view.mas_height).multipliedBy(0.2);
        make.left.equalTo(redView.mas_right).offset(20).priority(250);
    }];
  • 动画代码
[self.greenView removeFromSuperview];
    [UIView animateWithDuration:1.0f animations:^{
        [self.view layoutIfNeeded];
    }];

动画前

动画后
  • 好了,Masonry就为大家讲解到这里,如果对Masonry的使用语法或者是对约束的思想还有什么不理解,希望您可以不吝惜的提出来,我希望能把文章做得更通俗易懂,更深刻!
原文地址:https://www.cnblogs.com/yecong/p/6075584.html