iOS中Masonry的使用总结

Masonry

Masonry是一个基于AutoLayout的开源布局框架。类似的还有PureLayout,snapKit等。基于 frame的框架有Facade以及Neon。现在我们使用最多的就是MasonrySnapKit/SnapKit。以下,是对Masonry的使用总结。

核心方法有以下3个:

  • mas_makeConstraints: 负责添加约束

  • mas_updateConstraints: 更新约束

  • mas_remakeConstraints: 清除所有约束

需要注意的是,使用mas_makeConstrains方法的元素必须事先添加到父视图中

按比例取值:

  • multipliedBy属性:约束值与该值相乘
  • dividedBy属性:约束值与该值相除
    [_topView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self.view);
        make.width.equalTo(self.view.mas_width).dividedBy(2);
        make.height.equalTo(self.view.mas_width).multipliedBy(0.5);
    }];

dividedBy(2)与multipliedBy(0.5)的值是一样的。最终是一个居中的等宽高的正方形。

Masonry的基本使用-相对父视图布局

1.创建一个View,上下左右空出50个像素。我们可以使用offset来实现效果:

  [_topView mas_makeConstraints:^(MASConstraintMaker *make) {
          make.top.equalTo(self.view.mas_top).with.offset(50);
          make.left.equalTo(self.view.mas_left).with.offset(50);
          make.bottom.equalTo(self.view.mas_bottom).with.offset(-50);
          make.right.equalTo(self.view.mas_right).with.offset(-50);
      }];

也可以不指定约束边,默认取需要添加约束的边,简化写法如下:

    [_topView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view).mas_offset(50);
        make.left.equalTo(self.view).mas_offset(50);
        make.right.equalTo(self.view).mas_offset(-50);
        make.bottom.equalTo(self.view).mas_offset(-50);
    }];

还可以再简化些:

    [_topView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(50)
        make.left.equalTo(@50);
        make.bottom.equalTo(@(-50));
        make.right.equalTo(@(-50));
    }];

不声明相对视图,则默认值就是相对于依赖父视图对应相同约束的偏移量。如果你想使用字面量,那么就使用equalTo(@50)这种方式。如果不想使用字面量,那么就使用mas_equalTo(50)的方式。

还可以使用EdgeInsets来实现:

    [_topView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(50, 50, 50, 50));
      }];

同样的,也可以去除equalTo(self.view),那么也是相对于父视图添加约束。

  1. 创建一个View,水平居中,垂直居中但是向上偏移50像素。
    [_topView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.width.mas_equalTo(200);
        make.height.mas_equalTo(200);
        make.center.equalTo(self.view).centerOffset(CGPointMake(0, -100));
          //make.center.mas_equalTo(0).centerOffset(CGPointMake(0, -100));
    }];

3.有2个 view,topView 是 bottomView 的父视图。现在,我们使用bottomView来撑开父视图topView:

    [_topView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(20);
        make.right.mas_equalTo(-20);
        make.top.mas_equalTo(100);
    }];
    [_bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.mas_equalTo(50);
        make.right.bottom.mas_equalTo(-50);
        make.height.mas_equalTo(80);
    }];

Masonry的基本使用-相对兄弟视图布局

创建一个topView,水平居中,垂直居中但是向上偏移50像素。创建另一个 view,等宽等高,距离topView的底部100个像素。

    [_topView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.width.mas_equalTo(200);
        make.height.mas_equalTo(200);
        make.center.equalTo(self.view).centerOffset(CGPointMake(0, -100));
    }];
    [_bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.height.equalTo(_topView);
        make.top.equalTo(self.topView.mas_bottom).offset(100);
        make.left.and.right.equalTo(_topView);
    }];

mas_topLayoutGuidemas_bottomLayoutGuide的使用

mas_topLayoutGuide主要是针对导航栏布局,mas_bottomLayoutGuide则针对 tabbar 布局。

如果存在导航,那么我们设置topView 距离顶部为0,那么会发现 topView 会穿透到导航栏下面:

    [_topView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(20);
        make.right.mas_equalTo(-20);
        make.height.mas_equalTo(80);
        make.top.mas_equalTo(0);
    }];

你可以通过设置导航栏的来解决:

self.navigationController.navigationBar.translucent = NO;

或者手动加上导航栏的高度:

make.top.mas_equalTo(导航栏高度 + 偏移量);

当然,你可以使用mas_topLayoutGuide来布局:

    [_topView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(20);
        make.right.mas_equalTo(-20);
        make.height.mas_equalTo(80);
        make.top.mas_equalTo(self.mas_topLayoutGuide);
    }];

同理,如果你想距离 tabbar 进行布局,那么就使用mas_bottomLayoutGuide来进行布局。

masonry 中数组的使用

假如一个 view 的高度设置的是数组,那么会取数组中高度最低的那个值:

    _topView = [UIView new];
    _topView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:_topView];
    _bottomView = [UIView new];
    _bottomView.backgroundColor = [UIColor redColor];
    [self.view addSubview:_bottomView];
    [_topView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.mas_equalTo(0);
        make.width.height.mas_equalTo(100);
        make.top.mas_equalTo(self.mas_topLayoutGuide);
    }];
    [_bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.mas_equalTo(0);
        make.width.height.mas_equalTo(150);
        make.top.mas_equalTo(_topView.mas_bottom).offset(30);
    }];
    UIView *testView = [[UIView alloc]init];
    testView.backgroundColor = [UIColor orangeColor];
    [self.view addSubview:testView];
    [testView mas_makeConstraints:^(MASConstraintMaker *make) {
           make.centerX.mas_equalTo(0);
        make.width.height.mas_equalTo(@[_topView.mas_height, _bottomView.mas_height]);
        make.top.mas_equalTo(_bottomView.mas_bottom).offset(40);
       }];

testView 最终取的是100的值。

masonry 布局 UIScrollView

在使用masonry对UIScrollView设置约束时,我们不能直接设置 contentsize,我们需要使用一个 contentView 来撑起 contentSize。

    // 水平方向滚动视图
    UIScrollView *scrollView = ({
        UIScrollView *scrollView = [[UIScrollView alloc] init];
        scrollView.backgroundColor = UIColor.orangeColor;
        scrollView.pagingEnabled = YES;
        scrollView;
    });
    [self.view addSubview:scrollView];

    //设置 Scrollview 的约束
    [scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.insets(UIEdgeInsetsMake(150, 50, 50, 50));
    }];
    // 设置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);
    }];
    UIView *previousView = nil;
    for (int i = 0; i < 10; i++) {
        UILabel *label = ({
            UILabel *label = [[UILabel alloc] init];
            label.textAlignment = NSTextAlignmentCenter;
            label.numberOfLines = 2;
            label.backgroundColor = UIColor.redColor;
            label.text = [NSString stringWithFormat:@"水平方向\n第 %d 个视图", (i + 1)];
            label;
        });
        // 添加到父视图,并设置过渡视图中子视图的约束
        [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;
    }
    //设置将影响到scrollView的contentSize
    [contentView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.right.mas_equalTo(previousView.mas_right).offset(20);
    }];

使用 masonry 实现多列等宽

1.不指定每个 view 的宽度,根据视图间距,动态设置 view 的宽度:

    self.navigationController.navigationBar.translucent = NO;
    NSMutableArray *views = @[].mutableCopy;
            [_topView mas_makeConstraints:^(MASConstraintMaker *make) {
                make.edges.insets(UIEdgeInsetsMake(50, 50, 50, 50));
              }];
    for (NSInteger i = 0; i < 3; i++) {
        UILabel *label = ({
            UILabel *label = [[UILabel alloc] init];
            label.textAlignment = NSTextAlignmentCenter;
            label.numberOfLines = 2;
            label.backgroundColor = UIColor.redColor;
            label.text = [NSString stringWithFormat:@" %ld", (i + 1)];
            label;
        });
        [views addObject:label];
        [_topView addSubview:label];
    }
    [views mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerY.mas_equalTo(_topView);
        make.height.mas_equalTo(100);
    }];
    [views mas_distributeViewsAlongAxis:MASAxisTypeHorizontal
                       withFixedSpacing:10
                            leadSpacing:50
                            tailSpacing:100];

2.指定视图宽度,设置“头部”和“尾部”的间隔,中间间距自动计算:

    _topView = [UIView new];
    _topView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:_topView];
    self.navigationController.navigationBar.translucent = NO;
    NSMutableArray *views = @[].mutableCopy;
    [_topView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.insets(UIEdgeInsetsMake(50, 50, 50, 50));
    }];
    for (NSInteger i = 0; i < 3; i++) {
        UILabel *label = ({
            UILabel *label = [[UILabel alloc] init];
            label.textAlignment = NSTextAlignmentCenter;
            label.numberOfLines = 2;
            label.backgroundColor = UIColor.redColor;
            label.text = [NSString stringWithFormat:@" %ld", (i + 1)];
            label;
        });
        [views addObject:label];
        [_topView addSubview:label];
    }
    [views mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerY.mas_equalTo(_topView);
        make.height.mas_equalTo(70);
    }];
    [views mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedItemLength:50 leadSpacing:10 tailSpacing:40];

withFixedItemLength:50中,50就是指定的宽度。

2.设置宽高比例:

    _topView = [UIView new];
    _topView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:_topView];
    _bottomView = [UIView new];
    _bottomView.backgroundColor = [UIColor redColor];
    [self.topView addSubview:_bottomView];
    [_topView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.mas_equalTo(0);
        make.width.height.mas_equalTo(self.view.mas_width).multipliedBy(0.5);
    }];
    [_bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.mas_equalTo(0);
        make.width.mas_equalTo(_topView);
        //subView.height = subView.width * 0.5;
        make.height.mas_equalTo(_bottomView.mas_width).multipliedBy(0.5);
    }];

3.UILabel 与 UIButton 的自适应:

- (void)createDemo {
    self.contentLabel = [[UILabel alloc]init];
    self.contentLabel.tag = 100;
    [self.view addSubview:self.contentLabel];
    self.contentLabel.text = @"最近是用Masonry";
    self.contentLabel.backgroundColor = UIColor.redColor;
    //设置最大宽度约束
    [self.contentLabel setPreferredMaxLayoutWidth:self.view.bounds.size.width - 30];
    [self.contentLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
    self.contentLabel.numberOfLines = 0;
    [self.contentLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(100);
        make.left.mas_equalTo(15);
    }];

    UIButton *btn = [[UIButton alloc] init];
    self.btn = btn;
    btn.backgroundColor = [UIColor blueColor];
    btn.titleLabel.numberOfLines = 0;
    [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.contentLabel.mas_bottom).mas_offset(20);
        make.centerX.mas_equalTo(self.view);
        make.width.mas_lessThanOrEqualTo(self.view.mas_width).offset(-100);
        make.height.mas_greaterThanOrEqualTo(btn.titleLabel.mas_height);
//        make.height.mas_equalTo(@[btn.titleLabel.mas_height, btn.imageView.mas_height]);
    }];

}
- (void)clicked {
    self.contentLabel.text = [self.contentLabel.text stringByAppendingString:@"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"];
    [self.btn setTitle:[self.btn.titleLabel.text stringByAppendingString:@"BBBBBBBBBBBBBBBB"] forState:UIControlStateNormal];
}

动态修改约束

1.我们可以通过设置优先级来实现约束动画。比如删除一个视图:

    _topView = [UIView new];
    _topView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:_topView];
    _bottomView = [UIView new];
    _bottomView.backgroundColor = [UIColor redColor];
    [self.view addSubview:_bottomView];
    [_topView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.mas_equalTo(0);
        make.width.height.mas_equalTo(150);
        make.top.mas_equalTo(self.mas_topLayoutGuide);
    }];
    [_bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.mas_equalTo(0);
        make.width.height.mas_equalTo(150);
        make.top.mas_equalTo(_topView.mas_bottom).offset(30);
    }];
    UIView *testView = [[UIView alloc]init];
    testView.backgroundColor = [UIColor orangeColor];
    [self.view addSubview:testView];
    [testView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.mas_equalTo(0);
        make.width.height.mas_equalTo(150);
        make.top.mas_equalTo(_bottomView.mas_bottom).offset(40);
        make.top.mas_equalTo(_topView.mas_bottom).offset(40).priority(250);
    }];

删除视图:

    [_bottomView removeFromSuperview];
    [UIView animateWithDuration:1.0 animations:^{
        [self.view layoutIfNeeded];
    }];
  1. 通过卸载和装载约束来实现:

    声明2个全局变量:

    @property (nonatomic, strong)MASConstraint *constraint;
    @property (nonatomic, strong)MASConstraint *constraint1;

    实现:

        UIView *testView = [[UIView alloc]init];
        testView.backgroundColor = [UIColor orangeColor];
        [self.view addSubview:testView];
    
        _topView = [UIView new];
        _topView.backgroundColor = [UIColor blueColor];
        [self.view addSubview:_topView];
        _bottomView = [UIView new];
        _bottomView.backgroundColor = [UIColor redColor];
        [self.view addSubview:_bottomView];
        [_topView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.centerX.mas_equalTo(0);
            make.width.height.mas_equalTo(150);
            make.top.mas_equalTo(self.mas_topLayoutGuide);
        }];
        [_bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.centerX.mas_equalTo(0);
            make.width.height.mas_equalTo(150);
            _constraint1 = make.top.mas_equalTo(_topView.mas_bottom).offset(30);
            make.top.mas_equalTo(testView.mas_bottom).offset(30).priority(250);
        }];
    
        [testView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.centerX.mas_equalTo(0);
            make.width.height.mas_equalTo(150);
            _constraint = make.top.mas_equalTo(_bottomView.mas_bottom).offset(40);
            make.top.mas_equalTo(_topView.mas_bottom).offset(40).priority(250);
        }];

    点击卸载约束,则发现bottomView 和 testView 的位置发生了交换:

        [_constraint uninstall];
        [_constraint1 uninstall];
        [UIView animateWithDuration:1.0 animations:^{
            [self.view layoutIfNeeded];
        }];

   转载规则


《iOS中Masonry的使用总结》 刘星星 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
微信小程序总结提高 微信小程序总结提高
微信小程序总结提高现如今,微信小程序的开发越来越受欢迎,最近一段时间先后开发和维护了几个微信小程序,既有原生的,也有使用框架开发的.我现在维护的项目使用的框架有mpvue(现在已经迁移到uni-app)上,uni-app框架,Tar
下一篇 
React 基础使用 React 基础使用
使用 React 做一个 TodoList<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="vi
2019-03-11
  目录