masonry 使用笔记

为什么要做这个笔记

masonry 可以说是当前 iOS开发中最流行的 Autolayout框架,其极大的简化了苹果原生提供的AutoLayout语法。目前很多大厂也是用的这个自动布局框架,我司也在用。在此之前我一只在用另一个叫 SDAutolayout的自动布局库,所以 masonry 是个初学者,如果写得不好的地方还希望大佬指点江山。经过前段时间的开发项目,对masonry框架有了大致的认识,用法上面基本能解决所有布局问题。

masonry 基础约束用法

  • 首先先提及一下,view 在执行 masonry 布局之前必须添加到父视图上
  • 约束的写法有好几种,我先拿三种来举例
  1. 第一种。mas_equalTo()方法传入一个依赖的约束边,如下面示例中“self.view.mas_left”,后面跟着 mas_offset()方法可以传入一个值,这个值是相对于所依赖约束的偏移量。mas_offset()也可以不写,默认偏移0。
    UIView *demoView = [[UIView alloc] init];
    demoView.backgroundColor = UIColor.greenColor;
    [self.view addSubview:demoView];
    [demoView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(self.view.mas_left).mas_offset(50);//左边相对于 self.view 的 left 偏移50个单位
        make.right.mas_equalTo(self.view.mas_right).mas_offset(-50);//右边相对于 self.view 的 right 偏移-50个单位
        make.top.mas_equalTo(self.view.mas_top).mas_offset(100);//顶边相对于 self.view 的 top 偏移100个单位
        make.bottom.mas_equalTo(self.view.mas_bottom).mas_offset(-100);//底边相对于 self.view 的 bottom 偏移-100个单位
    }];
  1. 第二种。mas_equalTo()只需要传入相对的约束的视图,不需要指定约束边,默认取前面第一个需要添加约束的边
    UIView *demoView = [[UIView alloc] init];
    demoView.backgroundColor = UIColor.greenColor;
    [self.view addSubview:demoView];
    [demoView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(self.view).mas_offset(50);//等价于 make.left.mas_equalTo(self.view.mas_left).mas_offset(50);
        make.right.mas_equalTo(self.view).mas_offset(-50);//等价于 make.right.mas_equalTo(self.view.mas_right).mas_offset(-50);
        make.top.mas_equalTo(self.view).mas_offset(100);//等价于 make.top.mas_equalTo(self.view.mas_top).mas_offset(100);
        make.bottom.mas_equalTo(self.view).mas_offset(-100);//等价于 make.bottom.mas_equalTo(self.view.mas_bottom).mas_offset(-100);
    }];
  1. 第三种。mas_equalTo() 传入一个值,这个值就是相对于依赖父视图对应相同约束的偏移量
    UIView *demoView = [[UIView alloc] init];
    demoView.backgroundColor = UIColor.greenColor;
    [self.view addSubview:demoView];
    [demoView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(50);//等价于 make.left.mas_equalTo(demoView.superview.mas_left).mas_offset(50);
        make.right.mas_equalTo(-50);//等价于 make.right.mas_equalTo(demoView.superview.mas_right).mas_offset(-50);
        make.top.mas_equalTo(100);//等价于 make.top.mas_equalTo(demoView.superview.mas_top).mas_offset(100);
        make.bottom.mas_equalTo(-100);//等价于 make.bottom.mas_equalTo(demoView.superview.mas_bottom).mas_offset(-100);
    }];

三种写法结果是一致的,效果图如下

WechatIMG38.png

看你个人喜欢用哪一种,我个人比较懒,喜欢用第三种,所以都是以第三种写法作为描述

居左上

    YYLabel *leftTopLabel = [[YYLabel alloc] init];
    leftTopLabel.backgroundColor = UIColor.redColor;
    leftTopLabel.text = @"居左上";
    leftTopLabel.textColor = UIColor.whiteColor;
    leftTopLabel.textAlignment = NSTextAlignmentCenter;
    leftTopLabel.textVerticalAlignment = YYTextVerticalAlignmentCenter;
    [demoView addSubview:leftTopLabel];
    [leftTopLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_offset(0);
        make.top.mas_equalTo(0);
        make.height.mas_equalTo(100);
        make.width.mas_equalTo(100);
    }];

效果如下

WeChatc5eb1b8916c66b1c53873306fe0597b1.png

居左下

    YYLabel *leftBottomLabel = [[YYLabel alloc] init];
    leftBottomLabel.backgroundColor = UIColor.redColor;
    leftBottomLabel.text = @"居左下";
    leftBottomLabel.textColor = UIColor.whiteColor;
    leftBottomLabel.textAlignment = NSTextAlignmentCenter;
    leftBottomLabel.textVerticalAlignment = YYTextVerticalAlignmentCenter;
    [demoView addSubview:leftBottomLabel];
    [leftBottomLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_offset(0);
        make.bottom.mas_equalTo(0);
        make.height.mas_equalTo(100);
        make.width.mas_equalTo(100);
    }];

效果如下

WeChatb3f1265f09aadcd9e120047158e567cf.png

居右上

    YYLabel *rightTopLabel = [[YYLabel alloc] init];
    rightTopLabel.backgroundColor = UIColor.redColor;
    rightTopLabel.text = @"居右上";
    rightTopLabel.textColor = UIColor.whiteColor;
    rightTopLabel.textAlignment = NSTextAlignmentCenter;
    rightTopLabel.textVerticalAlignment = YYTextVerticalAlignmentCenter;
    [demoView addSubview:rightTopLabel];
    [rightTopLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.right.mas_offset(0);
        make.top.mas_equalTo(0);
        make.height.mas_equalTo(100);
        make.width.mas_equalTo(100);
    }];
WechatIMG41.jpeg

居右下

    YYLabel *rightBottomLabel = [[YYLabel alloc] init];
    rightBottomLabel.backgroundColor = UIColor.redColor;
    rightBottomLabel.text = @"居右下";
    rightBottomLabel.textColor = UIColor.whiteColor;
    rightBottomLabel.textAlignment = NSTextAlignmentCenter;
    rightBottomLabel.textVerticalAlignment = YYTextVerticalAlignmentCenter;
    [demoView addSubview:rightBottomLabel];
    [rightBottomLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.right.mas_offset(0);
        make.bottom.mas_equalTo(0);
        make.height.mas_equalTo(100);
        make.width.mas_equalTo(100);
    }];
WechatIMG42.jpeg

垂直水平居中

    YYLabel *centerLabel = [[YYLabel alloc] init];
    centerLabel.backgroundColor = UIColor.redColor;
    centerLabel.text = @"居中";
    centerLabel.textColor = UIColor.whiteColor;
    centerLabel.textAlignment = NSTextAlignmentCenter;
    centerLabel.textVerticalAlignment = YYTextVerticalAlignmentCenter;
    [demoView addSubview:centerLabel];
    [centerLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.mas_equalTo(0);
        make.height.mas_equalTo(100);
        make.width.mas_equalTo(100);
    }];
WechatIMG43 1.jpeg

子控件来撑起父控件的高度

平常在开发过程中,经常有这种情况就是父控件高度是根据子控件内容动态变化,用masonry来实现这个功能相当简单,这里有几个知识点;

  1. 约束类对象MASConstraint,通过点进mas_makeConstraints:方法的实现,源码如下
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    self.translatesAutoresizingMaskIntoConstraints = NO;
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    block(constraintMaker);
    return [constraintMaker install];
}

咋们暂且先不管 MASConstraintMaker 类,找到最后一句constraintMaker对象调用了 install 方法并返回;进入到这个install的实现,源码实现如下

- (NSArray *)install {
    if (self.removeExisting) {
        NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
        for (MASConstraint *constraint in installedConstraints) {
            [constraint uninstall];
        }
    }
    NSArray *constraints = self.constraints.copy;
    for (MASConstraint *constraint in constraints) {
        constraint.updateExisting = self.updateExisting;
        [constraint install];
    }
    [self.constraints removeAllObjects];
    return constraints;
}

其中最上面的那个判断条件在调用mas_remakeConstraints:方法时会被执行到,咋们也不看。咋们看下面这一段,其实 self.constraints 数组 就是在mas_makeConstraints:的 block 块中添加的所有约束对象,接下来 self.constraints 执行 copy 并遍历执行 constraint.updateExisting = self.updateExisting;[constraint install];,for 循环体中的第一句是标记更新约束还是新添加的约束,第二句是对约束装载,如果把第二句给注视掉,则添加的所有约束都不会被添加上,各位可以试一试;在方法的最后 返回了constraints数组,这个数组里面装的是 MASConstraint 类对象,再回到上面 mas_makeConstraints:方法中,最后的 return [constraintMaker install];实际上就是返回另一个约束对象的数组,没问题把。咋们在调用mas_makeConstraints:方法时可以用一个数组来接收,没毛病。。看代码

    UIView *superView = [[UIView alloc] init];
    superView.backgroundColor = UIColor.greenColor;
    [self.view addSubview:superView];
    NSArray <MASConstraint *>*constraints = [superView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_offset(20);
        make.right.mas_offset(-20);
        make.top.mas_offset(100);
    }];

咋们在来看两个方法,mas_offset()/mas_equalTo(),这两个方法其实就是一个宏,点进去看源码如此

#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__)))


#ifdef MAS_SHORTHAND_GLOBALS

#define equalTo(...)                     mas_equalTo(__VA_ARGS__)
#define greaterThanOrEqualTo(...)        mas_greaterThanOrEqualTo(__VA_ARGS__)
#define lessThanOrEqualTo(...)           mas_lessThanOrEqualTo(__VA_ARGS__)

#define offset(...)                      mas_offset(__VA_ARGS__)

#endif

mas_equalTo 对应 equalTo 方法,mas_offset 对应 valueOffset 方法,这两个其实就是 Block 执行体,返回是调用者对象(连式编程的标志性语法),这个对象也就是MASConstraint类对象。继续下去。篇幅有点长,这个知识点就到这里把。

  1. 约束的卸载uninstall,使用MASConstraint对象调用这个uninstall,就会把约束从对应的view上卸载掉;如下代码
    UIView *superView = [[UIView alloc] init];
    superView.backgroundColor = UIColor.greenColor;
    [self.view addSubview:superView];
    NSArray <MASConstraint *>*constraints = [superView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_offset(20);
        make.right.mas_offset(-20);
        make.top.mas_offset(100);
    }];
    //卸载约束
    for (MASConstraint *constraint in constraints) {
        [constraint uninstall];
    }

等价于

    UIView *superView = [[UIView alloc] init];
    superView.backgroundColor = UIColor.greenColor;
    [self.view addSubview:superView];
    __block MASConstraint *top = nil;
    __block MASConstraint *left = nil;
    __block MASConstraint *right = nil;
    [superView mas_makeConstraints:^(MASConstraintMaker *make) {
        left = make.left.mas_offset(20);
        right = make.right.mas_offset(-20);
        top = make.top.mas_offset(100);
    }];
    //卸载约束
    [left uninstall];
    [right uninstall];
    [top uninstall];
  1. 约束的装载install,使用MASConstraint对象调用这个install,就会把约束装载到对应的view上;如下代码
    UIView *superView = [[UIView alloc] init];
    superView.backgroundColor = UIColor.greenColor;
    [self.view addSubview:superView];
    NSArray <MASConstraint *>*constraints = [superView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_offset(20);
        make.right.mas_offset(-20);
        make.top.mas_offset(100);
    }];
    //装载约束
    //for (MASConstraint *constraint in constraints) {
    //   [constraint install];
    //}

等价于

    UIView *superView = [[UIView alloc] init];
    superView.backgroundColor = UIColor.greenColor;
    [self.view addSubview:superView];
    __block MASConstraint *top = nil;
    __block MASConstraint *left = nil;
    __block MASConstraint *right = nil;
    [superView mas_makeConstraints:^(MASConstraintMaker *make) {
        left = make.left.mas_offset(20);
        right = make.right.mas_offset(-20);
        top = make.top.mas_offset(100);
    }];
    //装载约束
    //[left install];
    //[right install];
    //[top install];

预备知识讲完了,开始完成功能

  1. 首先定义三个全局属性必须得用到的
@interface Demo2ViewController ()
@property (nonatomic, weak) UIView *superView;
@property (nonatomic, weak) UIView *bottomView;
@property (nonatomic, weak) MASConstraint *bottom;
@end
  1. 写一个createDemo方法来实现案列, 并调用这个方法
- (void)viewDidLoad {
    [super viewDidLoad];
    [self createDemo];
}

- (void)createDemo {
    //...案例代码在这里实现
}
  1. 创建 superView,不给 superView 高度,高度通过他的子视图给撑起来
- (void)createDemo {
   UIView *superView = [[UIView alloc] init];
    superView.backgroundColor = UIColor.greenColor;
    [self.view addSubview:superView];
    [superView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(20);
        make.right.mas_equalTo(-20);
        make.top.mas_equalTo(100);
    }];
}
    
  1. 创建一个添加按钮,没点击一下添加一个view在这个添加按钮下面
- (void)createDemo {
    
    /*。。。。*/
    
    UIButton *button = [[UIButton alloc] init];
    [button setTitle:@"增加" forState:UIControlStateNormal];
    button.backgroundColor = UIColor.redColor;
    [button addTarget:self action:@selector(action:) forControlEvents:UIControlEventTouchUpInside];
    [superView addSubview:button];
    [button mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(10);
        make.top.mas_equalTo(10);
        make.right.mas_equalTo(-10);
        self.bottom = make.bottom.mas_equalTo(-10);//记录下这个约束对象
        make.height.equalTo(@(100));
    }];
}
   
  1. superView的下方添加一个blueView,这个视图blueView的顶部始终距离superView的底部10
- (void)createDemo {
    
    /*。。。。*/
    
    UIView *blueView = [[UIView alloc] init];
    blueView.backgroundColor = UIColor.blueColor;
    [self.view addSubview:blueView];
    [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(20);
        make.right.mas_equalTo(-20);
        make.top.mas_equalTo(superView.mas_bottom).mas_offset(10);
        make.height.mas_equalTo(@(100));
    }];
}
    
  1. superView 赋值给 self.superView,把button赋值给self.bottomView
- (void)createDemo {
    
    /*。。。。*/
    
    self.superView = superView;
    self.bottomView = button;
}

运行效果图如下

WechatIMG44.png
  1. 实现添加方法action:,就能实现动态撑大父视图了
static int num = 0;
- (void)action:(UIButton *)button {
    //卸载旧的底部约束
    [self.bottom uninstall];
    num += 1;
    YYLabel *view = [[YYLabel alloc] init];
    view.backgroundColor = UIColor.redColor;
    view.text = [NSString stringWithFormat:@"控件%d", num];
    view.textAlignment = NSTextAlignmentCenter;
    view.textVerticalAlignment = YYTextVerticalAlignmentCenter;
    [self.superView addSubview:view];

    //给新 view 添加约束
    [view mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(self.superView.mas_left).offset(10);
        make.top.mas_equalTo(self.bottomView.mas_bottom).offset(10);
        make.right.mas_equalTo(self.superView.mas_right).offset(-10);
        //添加新的底部约束
        self.bottom = make.bottom.mas_equalTo(self.superView.mas_bottom).offset(-10);
        make.height.equalTo(@(100));
    }];
    self.bottomView = view;
}

效果图如下

Jan-18-2019 17-10-11.gif

masonry 在 UIScrollView 中的运用

masonry + UIScrollView 通过两个示例,一个是水平方向,另一个垂直方向,这也是平常开发中会遇到的。在使用masonry对UIScrollView设置约束后,contentSize属性就不管用了,而需要通过masonry 的规则在scrollView中添加一个 contentView,其它的子视图全部添加到 contentView 上,让contentView来撑起UIScrollview的 contentSize。

水平方向滑动

  1. 第一步创建一个Scrollview
    // 水平方向滚动视图
    UIScrollView *scrollView = [[UIScrollView alloc] init];
    scrollView.backgroundColor = UIColor.greenColor;
    scrollView.pagingEnabled = YES;
    [self.view addSubview:scrollView];
    //设置 Scrollview 的约束
    [scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(self.view).offset(100);
        make.left.mas_equalTo(10);
        make.right.mas_equalTo(-10);
        make.bottom.mas_equalTo(-100);
    }];
  1. 创建一个 contentView 放到Scrollview上,并设置约束
    // 设置scrollView的子视图,即过渡视图contentSize
    UIView *contentView = [[UIView alloc] init];
    [scrollView addSubview:contentView];
    [contentView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.mas_equalTo(scrollView);
        make.height.mas_equalTo(scrollView);
    }];
  1. 动态添加子视图到 contentView 上
    UIView *previousView = nil;
    for (int i = 0; i < 10; i++) {
        YYLabel *label = [[YYLabel alloc] init];
        label.textAlignment = NSTextAlignmentCenter;
        label.numberOfLines = 2;
        label.backgroundColor = UIColor.redColor;
        label.text = [NSString stringWithFormat:@"水平方向\n第 %d 个视图", (i + 1)];

        // 添加到父视图,并设置过渡视图中子视图的约束
        [contentView addSubview:label];
        [label mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(contentView).offset(20);
            make.bottom.equalTo(contentView).offset(-20);
            make.width.equalTo(scrollView).offset(-40);
            if (previousView) {
                make.left.mas_equalTo(previousView.mas_right).offset(40);
            } else {
                make.left.mas_equalTo(20);
            }
        }];
        previousView = label;
    }
  1. 设置 contentView 的right约束,这个是很关键的位置
    //设置将影响到scrollView的contentSize
    [contentView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.right.mas_equalTo(previousView.mas_right).offset(20);
    }];

运行结果如下

Jan-18-2019 17-52-54.gif

垂直方向滑动

垂直方向上跟水平方向上的实现方式大致相同,我就直接粘代码过来了


@interface Demo3ViewController ()
@property (nonatomic, weak) UIView *superView;
@property (nonatomic, weak) UIView *bottomView;
@property (nonatomic, weak) MASConstraint *bottom;
@end

@implementation Demo3ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self createDemo];
}

- (void)createDemo {
    UIScrollView *scrollView = [[UIScrollView alloc] init];
    scrollView.backgroundColor = UIColor.greenColor;
    [self.view addSubview:scrollView];
    [scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.mas_equalTo(0);
        make.right.mas_equalTo(-10.0);
        make.height.mas_equalTo(self.view).offset(-100);
    }];
    
    UIView *superView = [[UIView alloc] init];
    superView.backgroundColor = UIColor.greenColor;
    [scrollView addSubview:superView];
    [superView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(scrollView).with.insets(UIEdgeInsetsZero);
        make.width.mas_equalTo(scrollView);
    }];
    
    UIButton *button = [[UIButton alloc] init];
    [button setTitle:@"增加" forState:UIControlStateNormal];
    button.backgroundColor = UIColor.redColor;
    [button addTarget:self action:@selector(action:) forControlEvents:UIControlEventTouchUpInside];
    [superView addSubview:button];
    [button mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.mas_equalTo(0);
        make.height.mas_equalTo(100);
        make.top.mas_equalTo(20);
    }];
    
    self.superView = superView;
    self.bottomView = button;
    //设置 scrollview 的 contentSize
    [self.superView mas_makeConstraints:^(MASConstraintMaker *make) {
        self.bottom = make.bottom.mas_equalTo(self.bottomView.mas_bottom);
    }];
}

static int num = 0;
- (void)action:(UIButton *)button {
    //取消底部约束
    [self.bottom uninstall];
    num += 1;
    YYLabel *view = [[YYLabel alloc] init];
    view.backgroundColor = UIColor.redColor;
    view.text = [NSString stringWithFormat:@"控件%d", num];
    view.textAlignment = NSTextAlignmentCenter;
    view.textVerticalAlignment = YYTextVerticalAlignmentCenter;
    [self.superView addSubview:view];

    //给新 view 添加约束
    [view mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(self.superView).offset(20);
        make.right.mas_equalTo(self.superView).offset(-20);
        make.height.mas_equalTo(100);
        make.top.mas_equalTo(self.bottomView.mas_bottom).offset(20);
    }];
    
    //记录最下面的 view
    self.bottomView = view;
    
    //设置新的 contentSize
    [self.superView mas_makeConstraints:^(MASConstraintMaker *make) {
        self.bottom = make.bottom.mas_equalTo(self.bottomView.mas_bottom);
    }];
}

结果如下

Jan-18-2019 18-04-42.gif

masonry 多列等宽

固定间距

masonry提供了实现多列等高API,指定 item 之间的间距,然后 item 的宽或者高动态变化

/**
 *  distribute with fixed spacing
 *
 *  @param axisType     which axis to distribute items along
 *  @param fixedSpacing the spacing between each item
 *  @param leadSpacing  the spacing before the first item and the container
 *  @param tailSpacing  the spacing after the last item and the container
 */
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;

用法如下

- (void)createDemo {
    NSMutableArray *views = [NSMutableArray array];
    for (NSInteger i = 0; i < 3; i++) {
        YYLabel *item = [[YYLabel alloc] init];
        view.backgroundColor = UIColor.greenColor;
        view.textAlignment = NSTextAlignmentCenter;
        view.text = [NSString stringWithFormat:@"%ld",i];
        [views addObject:item];
        [self.view addSubview:item];
    }
    [views mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerY.mas_equalTo(self.view);
        make.height.mas_equalTo(100);
    }];
    [views mas_distributeViewsAlongAxis:MASAxisTypeHorizontal
                       withFixedSpacing:50
                            leadSpacing:10
                            tailSpacing:20];
}
WechatIMG46.jpeg

固定 item 宽度

masonry提供了实现多列等高API,指定 item 宽度或者高度,然后 item 之间的间距动态变化,方法描述

/**
 *  distribute with fixed item size
 *
 *  @param axisType        which axis to distribute items along
 *  @param fixedItemLength the fixed length of each item
 *  @param leadSpacing     the spacing before the first item and the container
 *  @param tailSpacing     the spacing after the last item and the container
 */
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;

用法

- (void)createDemo {
    NSMutableArray *views = [NSMutableArray array];
    for (NSInteger i = 0; i < 3; i++) {
        YYLabel *item = [[YYLabel alloc] init];
        item.backgroundColor = UIColor.greenColor;
        item.textAlignment = NSTextAlignmentCenter;
        item.text = [NSString stringWithFormat:@"%ld",i];
        [views addObject:item];
        [self.view addSubview:item];
    }
    [views mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerY.mas_equalTo(self.view);
        make.height.mas_equalTo(100);
    }];
    [views mas_distributeViewsAlongAxis:MASAxisTypeHorizontal
                    withFixedItemLength:100
                            leadSpacing:50
                            tailSpacing:50];
}
WechatIMG47.jpeg

masonry 设置宽高比例

在 masonry 提供了multipliedBy()函数用来处理布局比例问题
,API

/**
 *  Sets the NSLayoutConstraint multiplier property
 */
- (MASConstraint * (^)(CGFloat multiplier))multipliedBy;

用法

- (void)createDemo {
    //示例1
    YYLabel *superView = [[YYLabel alloc] init];
    superView.text = @"示例2";
    superView.textAlignment = NSTextAlignmentCenter;
    superView.backgroundColor = UIColor.redColor;
    [self.view addSubview:superView];
    [superView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.mas_equalTo(0);
        //superView.width = superView.height = self.view.width * 0.5;
        make.width.height.mas_equalTo(self.view.mas_width).multipliedBy(0.5);
    }];
    
    //示例2
    YYLabel *subView = [[YYLabel alloc] init];
    subView.text = @"示例2";
    subView.textAlignment = NSTextAlignmentCenter;
    subView.backgroundColor = UIColor.blueColor;
    [superView addSubview:subView];
    [subView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.mas_equalTo(0);
        make.width.mas_equalTo(superView);
        //subView.height = subView.width * 0.5;
        make.height.mas_equalTo(subView.mas_width).multipliedBy(0.5);
    }];
}

运行结果如下

WechatIMG48.jpeg

masonry UIButton 自适应宽度

在 UIButton 自使用中需要使用到 mas_greaterThanOrEqualTo(value) 以及 mas_lessThanOrEqualTo(value)方法,mas_greaterThanOrEqualTo(value) 大于等于 value,mas_lessThanOrEqualTo(value)则是小于等于value

示例如下

    UIButton *btn = [[UIButton alloc] init];
    btn.backgroundColor = [UIColor blueColor];
    [btn setTitle:@"UIButton宽度" forState:UIControlStateNormal];
    [btn setImage:[UIImage imageNamed:@"icon"] forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(clicked:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];
    [btn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.mas_equalTo(self.view);
        make.height.mas_equalTo(@[btn.titleLabel.mas_height, btn.imageView.mas_height]);
        make.width.mas_lessThanOrEqualTo(kScreenWidth);
    }];

clicked:方法实现

- (void)clicked:(UIButton *)btn {
    [btn setTitle:[btn.currentTitle stringByAppendingString:@"自适应"] forState:UIControlStateNormal];
}

看看效果怎么样

Jan-18-2019 20-06-16.gif

UIButton 还有很多玩法,这里就不一一举例了

masonry UILabel 自适应宽度/高度

  • 对于 UILabel 自适应宽度只需要使用 mas_lessThanOrEqualTo() 方法就能达到
  • 当宽度达到极限后开始换行需要设置两个参数,UILabel的 numberOfLines 属性和 preferredMaxLayoutWidth 属性,

代码实现

//普通文本
- (void)createDemo {
    self.label2 = [[UILabel alloc]init];
    self.label2.tag = 100;
    [self.view addSubview:self.label2];
    self.label2.text = @"最近是用Masonry";
    self.label2.backgroundColor = UIColor.redColor;
    [self.label2 setPreferredMaxLayoutWidth:self.view.width - 30];
    self.label2.numberOfLines = 0;
    [self.label2 setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
    [self.label2 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(100);
        make.left.mas_equalTo(15);
    }];

    UIButton *btn = [[UIButton alloc] init];
    btn.backgroundColor = [UIColor blueColor];
    [btn setTitle:@"添加文字" forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(clicked) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];
    [btn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(self.label2.mas_bottom).mas_offset(20);
        make.centerX.mas_equalTo(self.view);
        make.height.mas_equalTo(50);
        make.width.mas_lessThanOrEqualTo(kScreenWidth);
    }];
    
}
- (void)clicked {
    self.label2.text = [self.label2.text stringByAppendingString:@"Masonry自动布局UILabel"];
}

结果如下

Jan-18-2019 20-57-21.gif

masonry YYLabel 自适应宽度/高度

代码实现

- (void)createDemo {
    self.label1 = [[YYLabel alloc] init];
    self.label1.text = @"最近是用Masonry自动布局UILabel的时候,;这些东西之后,label还是没有换行。最近是用Masonry自动布局UILabel的时候,";
    self.label1.backgroundColor = UIColor.greenColor;
    self.label1.numberOfLines = 0;
    self.label1.preferredMaxLayoutWidth = self.view.width - 30;
    [self.view addSubview:self.label1];
    [self.label1 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(100);
        make.left.mas_equalTo(15);
    }];
    
    UIButton *btn = [[UIButton alloc] init];
    btn.backgroundColor = [UIColor blueColor];
    [btn setTitle:@"添加文字" forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(clicked) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];
    [btn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(self.label1.mas_bottom).mas_offset(20);
        make.centerX.mas_equalTo(self.view);
        make.height.mas_equalTo(50);
        make.width.mas_lessThanOrEqualTo(kScreenWidth);
    }];
    
}
- (void)clicked {
    self.label1.text = [self.label1.text stringByAppendingString:@"Masonry自动布局YYLabel"];
}

结果如下


Jan-18-2019 20-43-44.gif

masonry 实现动态 UITabelViewCell

先看效果

Jan-18-2019 21-03-23.gif

demo实现得很粗糙,但是基本功能都实现了。实现动态 UITabelViewCell 需要了解到的知识点

  1. 不设置cell的高度
  2. 不实现返回cell高度的代理方法
  3. 设置 UITabelViewCell的estimatedRowHeight属性
  4. 将tableview的rowHeight属性设置为UITableViewAutomaticDimension

示例如下

- (UITableView *)tableView {
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
        _tableView.delegate = self;
        _tableView.dataSource = self;
        _tableView.estimatedRowHeight = 100;
        _tableView.rowHeight = UITableViewAutomaticDimension;
        [_tableView registerClass:Demo8Cell.class forCellReuseIdentifier:@"cellID"];
        [self.view addSubview:_tableView];
    }
    return _tableView;
}

关于 tableview 需要设置的就这些,剩下的在 tableviewCell 的动态高度在cell内部设置

关于 Demo8Cell 内部实现如下

.h

@class Demo8Model;
NS_ASSUME_NONNULL_BEGIN

@interface Demo8Cell : UITableViewCell
@property (nonatomic, strong) UILabel *messageLabel;

- (void)setmessage:(Demo8Model *)message;
@end

.m

@interface Demo8Cell ()
@property (nonatomic, strong) UIView *picContentView;
@property (nonatomic, strong) UIView *timerView;
@property (nonatomic, strong) UIView *likeView;
@end

@implementation Demo8Cell

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier  {
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        [self createUI];
    }
    return self;
}

- (void)createUI {
    self.messageLabel = [[UILabel alloc] init];
    self.messageLabel.numberOfLines = 0;
    [self.contentView addSubview:self.messageLabel];
    [self.messageLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(8);
        make.left.mas_equalTo(10);
        make.right.mas_equalTo(-10);
    }];
    self.picContentView = [[UIView alloc]init];
    self.picContentView.backgroundColor = UIColor.grayColor;
    [self.contentView addSubview:self.picContentView];
    [self.picContentView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(self.messageLabel.mas_bottom).offset(15);
        make.left.mas_offset(50);
        make.right.mas_offset(-20);
    }];
    
    self.timerView = [[UIView alloc]init];
    self.timerView.backgroundColor = UIColor.redColor;
    [self.contentView addSubview:self.timerView];
    [self.timerView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(self.picContentView.mas_bottom).mas_offset(10);
        make.left.mas_equalTo(50);
        make.height.mas_equalTo(30);
        make.width.mas_equalTo(50);
        //关键位置
        make.bottom.mas_equalTo(-8);
    }];
    
    self.likeView = [[UIView alloc] init];
    self.likeView.backgroundColor = UIColor.redColor;
    [self.contentView addSubview:self.likeView];
    [self.likeView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(self.picContentView.mas_bottom).mas_offset(10);
        make.right.mas_equalTo(-20);
        make.height.mas_equalTo(30);
        make.width.mas_equalTo(50);
    }];
}
- (void)setmessage:(Demo8Model *)message {
    // 创建一个可变属性字符串
    NSMutableAttributedString *finalStr = [[NSMutableAttributedString alloc] init];
    
    // 创建姓名
    NSAttributedString *nameStr = [[NSAttributedString alloc] initWithString:message.name attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:16], NSForegroundColorAttributeName: [UIColor redColor]}];
    
    // 创建发言内容
    NSAttributedString *messageStr = [[NSAttributedString alloc] initWithString:message.message attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:16], NSForegroundColorAttributeName: [UIColor blackColor]}];
    
    // 拼接上两个字符串
    [finalStr appendAttributedString:nameStr];
    [finalStr appendAttributedString:messageStr];
    self.messageLabel.attributedText = finalStr;
    //移除所有图片
    [self.picContentView removeAllSubviews];
    [self createDemo:message.picNum];
}

- (void)createDemo:(NSInteger)itemNum {
    //假设要显示 num 个item
    NSInteger num = itemNum;
    //每行显示的个数
    NSInteger count = 3;
    //显示的总行数
    NSInteger rowNum = (num/count) + ((NSInteger)(num%count>0));
    UIView *lastView = nil;
    for (int i = 0; i < rowNum; i ++) {
        NSMutableArray *masonryViewArray = [NSMutableArray array];
        for (int j = 0; j < count; j ++) {
            UIView *view = [[UIView alloc] init];
            if ((i * count) + j > num-1) {
                view.backgroundColor = [UIColor clearColor];
            } else {
                view.backgroundColor = [UIColor redColor];
            }
            [self.picContentView addSubview:view];
            [masonryViewArray addObject:view];
            lastView = view;
        }
        // 固定 item 之间的间距,item 的宽或者高自动缩放
        [masonryViewArray mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:30 leadSpacing:10 tailSpacing:10];
        // 设置array的垂直方向的约束
        [masonryViewArray mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(@((100 * i) + 10));
            make.height.equalTo(@80);
        }];
    }
    if (lastView) {
        [self.picContentView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.bottom.mas_equalTo(lastView.mas_bottom).mas_offset(20);
        }];
    }
}
@end

未完待续。。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容