Masonry的基本使用

本人有若干成套学习视频, 可试看! 可试看! 可试看, 重要的事情说三遍 包含Java, 数据结构与算法, iOS, 安卓, python, flutter等等, 如有需要, 联系微信tsaievan.

说到Masonry的使用, 相信大家一定不会陌生, 但是我在使用Masonry的过程中还是有些懵逼的地方, 比如使用什么API, 在哪个地方添加约束等. 所以我就把Masonry的官方demo抄了一遍, 发现还是有很多值得注意的地方, 现在总结如下:

官方的Demo是严格将控制器和View分离了的, 你也可以理解为严格的MVC架构, 只是缺少了Model层.

对于一些简单的约束, 可以将约束直接写在init方法里面, 或者initWithFrame:指定构造方法里面

还需要注意的一点是:

注意: 添加子控件一定要放在设置约束之前, 不然约束就找不到约束对象

(一)最基本的使用:

先看如下代码:
        
        UIView *superView = self;
        int padding = 10;
        [greenView mas_makeConstraints:^(MASConstraintMaker *make) {
            
            // 大于等于
            make.top.mas_greaterThanOrEqualTo(superView.mas_top).offset(padding);
            make.left.mas_equalTo(superView.mas_left).offset(padding);
            make.bottom.mas_equalTo(blueView.mas_top).offset(-padding);
            make.right.mas_equalTo(redView.mas_left).offset(-padding);
            make.width.mas_equalTo(redView.mas_width);
            make.height.mas_equalTo(redView.mas_height);
            make.height.mas_equalTo(blueView.mas_height);
        }];

这也是我们最常用的几个API:

  • mas_makeConstraints
  • mas_remakeConstraints
  • mas_updateConstraints

稍后还会详细说这几个API

在我们设置约束的时候, 通常会用到

  • mas_greaterThanOrEqualTo--------------≥
  • mas_lessThanOrEqualTo-----------------≤
  • mas_equalTo---------------------------=

这里面的参数可以传的类型很多, 你可以传一个UIView类型的指针, 也可以传MASViewAttribute类型的属性, 可以传一个包含多个View的数组, 也可以传包含多个MASViewAttribute类型的数组:

            // 可以传一个包含views的数组
            make.height.mas_equalTo(@[ greenView, blueView ]);
            // 可以传一个包含属性的数组
            make.height.mas_equalTo(@[ greenView.mas_height, redView.mas_height ]);

(二)常量

  • 使用NSNumber类型以及CGFloat类型
        [purpleView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.mas_equalTo(20);
            make.left.mas_equalTo(20);
            make.bottom.mas_equalTo(-20);
            make.right.mas_equalTo(-20);
        }];

你也可以将CGFloat类型的包装成NSNumber类型

        [purpleView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.mas_equalTo(@20);
            make.left.mas_equalTo(@20);
            make.bottom.mas_equalTo(@-20);
            make.right.mas_equalTo(@-20);
        }];
  • 使用标量和结构体
        // 还可以使用标量和结构体
        [orangeView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.center.mas_equalTo(CGPointMake(0, 50));
            make.size.mas_equalTo(CGSizeMake(200, 100));
        }];
make.edges.mas_equalTo(lastView).insets(UIEdgeInsetsMake(5, 10, 15, 20));

(三)Edges的使用

根据我自己的理解, 使用edges如果得当的话, 会省去一些代码, 因为你甚至可以不用一一地设置top, left, bottom, right. 因为这些都可以在edges中设置好, 只要是top, left, bottom, right的属性, 都可以通过edges的insets属性来设置:

- (instancetype)init {
    if (self = [super init]) {
        UIView *lastView = self;
        for (NSInteger i = 0; i < 10; i++) {
            UIView *view = [[UIView alloc] init];
            
            /* 添加子控件一定要放在设置约束之前, 不然约束就找不到约束对象 */
            [self addSubview:view];
            view.backgroundColor = [UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1];
            view.layer.borderColor = [UIColor blackColor].CGColor;
            view.layer.borderWidth = 2;
            [view mas_makeConstraints:^(MASConstraintMaker *make) {
                make.edges.mas_equalTo(lastView).insets(UIEdgeInsetsMake(5, 10, 15, 20));
            }];
            lastView = view;
        }
    }
    return self;
}

实现效果:


实现效果

(四)自适应

在设置view大小的过程中, 我们往往不需要同时设置宽和高的具体值, 只需要设置宽高比, 但宽或者高, 一方的高度确定了, 另一方的高度也确定了.

        // 布局顶部和底部的view, 使之各占据屏幕的1/2
        [self addSubview:self.topView];
        [self.topView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.left.mas_equalTo(self);
            make.right.mas_equalTo(self).offset(-1);
        }];
        
        [self addSubview:self.bottomView];
        [self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.bottom.right.mas_equalTo(self);
            make.top.mas_equalTo(self.topView.mas_bottom).offset(1);
            make.height.mas_equalTo(self.topView);
        }];
        
        // 内部的view按照3:1的比例自适应
        [self.topView addSubview:self.topInnerView];
        [self.topInnerView mas_makeConstraints:^(MASConstraintMaker *make) {
            
            // 宽是高的3倍
            make.width.mas_equalTo(self.topInnerView.mas_height).multipliedBy(3);
            
            // 宽和高不大于父控件
            make.width.and.height.mas_lessThanOrEqualTo(self.topView);
            
            // 宽和高等于父控件, 优先级低. 所以遇到上面的约束的时候, 产生的效果就是: 宽等于父控件, 高等于宽的1/3
            make.width.and.height.mas_equalTo(self.topView).with.priorityLow();
            make.center.mas_equalTo(self.topView);
        }];
        
        [self.bottomView addSubview:self.bottomInnerView];
        [self.bottomInnerView mas_makeConstraints:^(MASConstraintMaker *make) {
        
            // 高是宽的3倍
            make.height.mas_equalTo(self.bottomInnerView.mas_width).multipliedBy(3);
            
            // 宽和高不大于父控件
            make.width.and.height.mas_lessThanOrEqualTo(self.bottomView);
            
            // 宽和高等于父控件, 优先级低, 所以遇到上面的约束的时候, 产生的效果就是: 高等于父控件, 宽是高的1/3
            make.width.and.height.mas_equalTo(self.bottomView).with.priorityLow();
            make.center.mas_equalTo(self.bottomView);
        }];

这段代码里涉及到了:

  • multipliedBy 倍数
  • priorityLow 优先级
    的设置

(五)利用约束的变化制作动画

如下代码所示, self.animatableConstraints是一个放约束的数组, 数组中的约束的设置是有效的.

        [greenView mas_makeConstraints:^(MASConstraintMaker *make) {
            [self.animatableConstraints addObjectsFromArray:@[
                                                              make.edges.mas_equalTo(superView).insets(paddingInsets).priorityLow(),
                                                              make.bottom.mas_equalTo(blueView.mas_top).offset(-padding)
                                                              ]];
            make.size.mas_equalTo(redView);
            make.height.mas_equalTo(blueView.mas_height);
        }];

然后需要变化约束的时候, 遍历这个数组, 取出其中的约束, 赋上新值

    int padding = invertedInsets ? 100 : self.padding;
    UIEdgeInsets paddingInsets = UIEdgeInsetsMake(padding, padding, padding, padding);
    for (MASConstraint *constraint in self.animatableConstraints) {
        
        /**
         *  Modifies the NSLayoutConstraint constant,
         *  only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following
         *  NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight
         */
        
        // 修改了NSLayoutConstraint的约束,
        // 只影响MASConstraints对象中第一个控件的NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight属性
        // 即上, 左, 下, 右属性
        constraint.insets = paddingInsets;
    }

最后将约束的更改设置成动画即可, 基本思路就是这样:

    [UIView animateWithDuration:2 animations:^{
        [self layoutIfNeeded];
    } completion:^(BOOL finished) {
        // 重复!
        [self animateWithInvertedInsets:!invertedInsets];
    }];

(六)消除Bug

在我们设置约束的时候, 免不了会约束冲突. 在纷繁复杂的控制台打印中无法知道自己究竟哪几个约束冲突了. 这时候我们就需要有准确的定位. Masonry为我们准备了这一切:
  • 可以将解bug的key与各个view关联起来:
        greenView.mas_key = @"greenView";
        redView.mas_key = @"redView";
        blueView.mas_key = @"blueView";
        superView.mas_key = @"superView";
  • 以上代码等同于
MASAttachKeys(greenView, redView, blueView, superView);

就这么一个强大的宏, 就把相应的view跟他们的名称绑定在一起了.

  • 除此之外, 约束也可以绑定key
make.edges.mas_equalTo(1).key(@"conflicting constraints");
make.height.mas_greaterThanOrEqualTo(5000).key(@"constant constraints");
  • 甚至NSNumber类型的也可以作为key
make.height.mas_equalTo(redView.mas_height).key(@31587435);

在绑定了key之后, 再出现约束冲突之后, 我们就能够很容易的发现, 究竟是哪些约束冲突了:

约束冲突

(七)Label的约束

这里涉及到一个新的属性, 就是基线对齐:
[self.shortLabel mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.mas_equalTo(self.longLabel.mas_baseline);
            make.right.mas_equalTo(self.mas_right).offset(-10);
        }];

短的Label的顶部和长Label的最后一条基线对齐

在demo中是动态地设置长label的最大宽度的, 这个时候需要计算长label的最大宽度. 我们首先要super调用layoutSubviews, 拿到长和短label的frame的值, 拿到frame值之后, 计算出preferredMaxLayoutWidth的值, 然后重新计算长短Label的frame值:

- (void)layoutSubviews {
    [super layoutSubviews];
    
    // 多行的label必须设置`preferredMaxLayoutWidth`属性的值
    // 必须在调用了super之后设置, 因为只有在这个时候, 才能拿到自动布局的值
    CGFloat width = CGRectGetMinX(self.shortLabel.frame) - 10;
    width -= CGRectGetMinX(self.longLabel.frame);
    self.longLabel.preferredMaxLayoutWidth = width;
    
    // 需要再次调用super, 以便系统根据`preferredMaxLayoutWidth`的值再次计算frame
    [super layoutSubviews];
}

但是第一次进入页面的时候会因为重新布局而闪一下, 目前还没有找到好的解决办法.

(八)更新约束

在应用中我们常常需要更新约束, 在我们需要更新约束的时候, 有两个关键的方法需要调用:

    // 告诉约束他们需要更新
    [self setNeedsUpdateConstraints];
    
    // 更新约束, 以便我们可以将这种变化做成动画
    [self updateConstraintsIfNeeded];

当我们调用了这两个方法之后, 就会来到

- (void)updateConstraints

这个方法, 这个方法是苹果官方推荐更新约束方法:

// 苹果官方推荐的添加和更改约束的地方 /** 在本例中, 所有的约束都写在这个方法里 */
- (void)updateConstraints {
    [self.growingButton mas_updateConstraints:^(MASConstraintMaker *make) {
        make.center.mas_equalTo(self);
        
        /****************** -------- 设置优先级为低 -------- ******************/
         /* 
          
          宽和高都是小于等于父控件的,这是大前提
          然后设置约束为buttonSize的大小
          当buttonSize 不断增大, 要大于父控件的宽高时, 这是不允许的
          因为宽高小于等于父控件的优先级高, 所以最终的结果是宽高等于父控件k
          
          */
        make.width.mas_equalTo(self.buttonSize.width).priorityLow();
        make.height.mas_equalTo(self.buttonSize.height).priorityLow();
        make.width.height.mas_lessThanOrEqualTo(self);
    }];
    
    
    // 根据苹果的约定, 在方法的结尾一定要调用super /** 注意: 是结尾 */
    [super updateConstraints];
}

在这个方法中, 我们写需要更新的约束!

  • mas_updateConstraints: 更新约束, 是以前的约束依然在, 新变化的约束将替换旧的约束, 没有变化的约束依然在, 并且不会被重复添加

  • mas_makeConstraints: 添加约束, 以前旧的约束不会被覆盖, 多次调用后, 会重复添加很多约束

  • mas_remakeConstraints: 重建约束, 这个会去除掉控件上原始的所有约束, 将新的约束添加上去

在这个demo中, 只能写更新约束:
- (void)updateConstraints {
    
    MASAttachKeys(_growingButton, self);
    
    /****************** -------- 这里必须写更新约束: mas_updateConstraints -------- ******************/
    
    
    /****************** -------- 如果写的是重建约束 -------- ******************/
    
    //    [self.growingButton mas_remakeConstraints:^(MASConstraintMaker *make) {
    //        make.center.mas_equalTo(CGPointMake(0, 0));
    //        make.width.mas_equalTo(self.buttonSize.width).priorityLow();
    //        make.height.mas_equalTo(self.buttonSize.height).priorityLow();
    //        make.width.mas_lessThanOrEqualTo(self);
    //        make.height.mas_lessThanOrEqualTo(self);
    //    }];
    
    /*
     * 那么在:
     *     make.width.mas_equalTo(self.buttonSize.width).priorityLow();
     *     make.height.mas_equalTo(self.buttonSize.height).priorityLow();
     * 这两句代码将约束的优先级设置为低,去掉这两个约束之后,button的大小变为自适应buttonTitle大小
     * 当更新约束的时候, 由于之前buttonTitle大小的约束是优先级高的约束, 所以这两个优先级低的约束是加不上去的
     *
     *****************************************************************************************
     *
     * 当去掉优先级为低之后, 代码变成
     *     make.width.mas_equalTo(self.buttonSize.width;
     *     make.height.mas_equalTo(self.buttonSize.height;
     * 这样代码就看似没问题了, 但是还是会有一个bug:
     * 就是当button变大到比父控件还大的时候,就会和下面这两句冲突:
     *     make.width.mas_lessThanOrEqualTo(self);
     *     make.height.mas_lessThanOrEqualTo(self);
     */
    
    /****************** -------- 如果写的是新建约束 -------- ******************/
    
    //    [self.growingButton mas_makeConstraints:^(MASConstraintMaker *make) {
    //        make.center.mas_equalTo(CGPointMake(0, 0));
    //        make.width.mas_equalTo(self.buttonSize.width).priorityLow();
    //        make.height.mas_equalTo(self.buttonSize.height).priorityLow();
    //        make.width.mas_lessThanOrEqualTo(self);
    //        make.height.mas_lessThanOrEqualTo(self);
    //    }];
    
    /*
     * 那么之前的约束通通不会消失, 依然加载控件上
     * 连续点击Button按钮之后, 约束越来越多.还会出现一个问题:
     * 1>  第一次要点3次按钮, 按钮才会增大
     * 2>  后来要点2次按钮, 按钮才会增大
     */
    
    /****************** -------- 所以要写更新约束 -------- ******************/
    /*
     * 更新约束, 之前没变的约束会一直在,不会重复添加
     * 变化的约束会将之前的约束移除, 然后添加新的约束上去
     */
    
    [self.growingButton mas_updateConstraints:^(MASConstraintMaker *make) {
        //        make.center.mas_equalTo(self);
        /* 上面这句代码还可写成 */
        make.center.mas_equalTo(CGPointMake(0, 0)); // 这也可以表示button处于父控件的中心点位置
        
        /* 下面的代码表示, 宽和高为我设置的buttonSize的大小, 但是不断增大后, button的大小依然小于等于父控件的大小 */
        
        //        make.width.mas_equalTo(self.buttonSize.width).priorityLow();
        //        make.height.mas_equalTo(self.buttonSize.height).priorityLow();
        
        /* 上面的两句代码还可以简写成: */
        make.size.mas_equalTo(self.buttonSize).priorityLow();
        
        make.width.mas_lessThanOrEqualTo(self);
        make.height.mas_lessThanOrEqualTo(self);
    }];
    
    // 根据苹果的规定, 必须在方法的结尾调用`super`
    [super updateConstraints];
}

关于更新约束,
特别要注意的有两点:
  1. 在设置了约束之后, 一定要调用super!
  2. 当界面一进来的时候, 就要调用
- (void)updateConstraints

这个方法, 但系统并不会自动调用这个方法, 那么我们就需要告诉系统, 我确实使用的是自动布局, 我需要界面一进来就调用这个方法, 那么我就要打开下面这个方法, 并返回YES:

/****************** -------- 在本例中, 这句代码必须retrun YES -------- ******************/
/*
 * 这句代码是告诉系统, 我确实是进行时自动布局的, 这样才会一开始就调用
 * `updateConstraints`方法
 */
+ (BOOL)requiresConstraintBasedLayout {
    return YES;
}

(九)重建约束

重建约束将去掉控件之前所有的约束
- (void)updateConstraints {
     /*
      * `mas_remakeConstraints` 这个方法是去掉控件之前所有的约束
      */
    [self.movingButton mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.size.mas_equalTo(CGSizeMake(100, 100));
        if (self.topLeft) {
            make.top.and.left.mas_equalTo(self).with.offset(10);
        }else {
            make.bottom.and.right.mas_equalTo(self).with.offset(-10);
        }
    }];
    [super updateConstraints];
}

(十)ScrollView

ScrollView的约束本身是有点坑的.
  • 首先, scrollView的大小是跟父控件大小相等的:
[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.edges.mas_equalTo(self);
        }];
  • 然后scrollView内部新建一个contentView, 这个contentView实际上是跟scrollView的contentSize的大小一样的. 先设置contentView的边缘和scrollView对齐, 宽度跟scrollView相等.
    [contentView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.mas_equalTo(self.scrollView);
        make.width.mas_equalTo(self.scrollView);
    }];
  • 在contentView内部循环添加view
    for (NSUInteger i = 0; i < 10; i++) {
        UIView *view = [[UIView alloc] init];
        view.backgroundColor = [UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1];
        [contentView addSubview:view];
        
        UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)];
        [view addGestureRecognizer:singleTap];
        
        [view mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.mas_equalTo(lastView ? lastView.mas_bottom : @0);
            make.left.mas_equalTo(0);
            make.width.mas_equalTo(contentView.mas_width);
            make.height.mas_equalTo(height);
        }];
        
        height += 25;
        lastView = view;
    }
  • 最后, "封口", contentView的底部跟最后一个View的底部对齐:
    [contentView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.mas_equalTo(lastView.mas_bottom);
    }];

(十一)链式编程

Masonry最大的特点就是链式编程:
make.top.and.left.mas_equalTo(superView).insets(padding);

and, with这些都是方便可读性的, 是可选的, 可要可不要.

(十二)StackView

在Masonry中有一个类似于StackView的布局

主要方法有两个:

方法一:

/**
 *  distribute with fixed spacing
 *
 *  @param axisType     views分布的方向
 *  @param fixedSpacing views之间的间距
 *  @param leadSpacing  第一个view与容器之间的距离
 *  @param tailSpacing  最后一个view与容器之间的距离
 */
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;

方法二:

/**
 *  distribute with fixed item size
 *
 *  @param axisType        views分布的方向
 *  @param fixedItemLength 在分布方向上views的长度
 *  @param leadSpacing     第一个view和容器之间的距离
 *  @param tailSpacing     最后一个view和容器之间的距离
 */
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
- (instancetype)init {
    if (self = [super init]) {
        NSMutableArray *arr = @[].mutableCopy;
        for (NSInteger i = 0; i < 4; i++) {
            UIView *view = [[UIView alloc] init];
            view.backgroundColor = [UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1];
            view.layer.borderColor = [UIColor blackColor].CGColor;
            view.layer.borderWidth = 2;
            [self addSubview:view];
            [arr addObject:view];
        }
        unsigned int type = arc4random() % 4;
        switch (type) {
            case 0:
                 /* 
                  * 参数1 : 排布方向 : 水平
                  * 参数2 : 控件间距 : 20
                  * 参数3 : 在排布方向(水平)上, 第一个控件和容器的间距(头部间距) : 5
                  * 参数4 : 在排布方向(水平)上, 最后一个控件和容器的间距(尾部间距) : 5
                  */
                [arr mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:20 leadSpacing:5 tailSpacing:5];
                
                // 只需要设置顶部间距, 和高度即可
                [arr mas_makeConstraints:^(MASConstraintMaker *make) {
                    make.top.mas_equalTo(60);
                    make.height.mas_equalTo(60);
                }];
                
                NSLog(@"type : %d", type);
                break;
                
            case 1:
                /*
                 * 参数1 : 排布方向 : 垂直
                 * 参数2 : 控件间距 : 20
                 * 参数3 : 在排布方向(垂直)上, 第一个控件和容器的间距(头部间距) : 5
                 * 参数4 : 在排布方向(垂直)上, 最后一个控件和容器的间距(尾部间距) : 5
                 */
                [arr mas_distributeViewsAlongAxis:MASAxisTypeVertical withFixedSpacing:20 leadSpacing:5 tailSpacing:5];
                
                // 只需要设置左边间距, 和宽度即可
                [arr mas_makeConstraints:^(MASConstraintMaker *make) {
                    make.left.mas_equalTo(0);
                    make.width.mas_equalTo(60);
                }];
                NSLog(@"type : %d", type);
                break;
                
            case 2:
                /*
                 * 参数1 : 排布方向 : 水平
                 * 参数2 : 控件在排布方向上的长度(宽度) : 30
                 * 参数3 : 在排布方向(水平)上, 第一个控件和容器的间距(头部间距) : 200
                 * 参数4 : 在排布方向(水平)上, 最后一个控件和容器的间距(尾部间距) : 30
                 */
                [arr mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedItemLength:30 leadSpacing:200 tailSpacing:30];
                
                // 只需要设置顶部间距, 和高度即可
                [arr mas_makeConstraints:^(MASConstraintMaker *make) {
                    make.top.mas_equalTo(60);
                    make.height.mas_equalTo(60);
                }];
                NSLog(@"type : %d", type);
                break;
                
            case 3:
                /*
                 * 参数1 : 排布方向 : 垂直
                 * 参数2 : 控件在排布方向上的长度(高度) : 30
                 * 参数3 : 在排布方向(垂直)上, 第一个控件和容器的间距(头部间距) : 30
                 * 参数4 : 在排布方向(垂直)上, 最后一个控件和容器的间距(尾部间距) : 200
                 */
                [arr mas_distributeViewsAlongAxis:MASAxisTypeVertical withFixedItemLength:30 leadSpacing:30 tailSpacing:200];
                
                // 只需要设置左边间距, 和宽度即可
                [arr mas_makeConstraints:^(MASConstraintMaker *make) {
                    make.left.mas_equalTo(0);
                    make.width.mas_equalTo(60);
                }];
                NSLog(@"type : %d", type);
                break;
                
            default:
                break;
        }
    }
    return self;
}

(十三)强引用约束

约束是可以作为属性的
// in public/private interface
@property (nonatomic, strong) MASConstraint *topConstraint;

...

// when making constraints
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    self.topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top);
    make.left.equalTo(superview.mas_left).with.offset(padding.left);
}];

...
// then later you can call
[self.topConstraint uninstall];

当你需要解除约束的时候, 就可以将其unstall.

以上就是Masonry的基本用法.

PS. 本人有若干成套学习视频, 包含Java, 数据结构与算法, iOS, 安卓, python, flutter等等, 如有需要, 联系微信tsaievan.

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

推荐阅读更多精彩内容