iOS视图

iOS Masonry

2019-07-25  本文已影响0人  Coder_Cat

Masonry简介

Masonry主要的类和常用api

//寻找两个视图的最近的公共父视图(类比两个数字的最小公倍数)
- (instancetype)mas_closestCommonSuperview:(MAS_VIEW *)view;
//创建添加安装约束
- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
//更新已经存在的约束(若约束不存在就直接安装)
- (NSArray *)mas_updateConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
//重置约束,移除原来已经创建的约束并添加上新的约束
- (NSArray *)mas_remakeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
#import "MASUtilities.h"
// 允许使用可链接语法创建约束。 约束可以表示单个NSLayoutConstraint(MASViewConstraint)
// 或一组NSLayoutConstraints(MASComposisteConstraint)
@interface MASConstraint : NSObject

// 修改NSLayoutConstraint常数,仅仅影响第一个item的NSLayoutAttribute
// 是下面NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, 
// NSLayoutAttributeRight之一的MASConstraints
- (MASConstraint * (^)(MASEdgeInsets insets))insets;

// 修改NSLayoutConstraint常数,仅仅影响第一个item的NSLayoutAttribute
// 是下面NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, 
// NSLayoutAttributeRight之一的MASConstraints
- (MASConstraint * (^)(CGFloat inset))inset;

// 修改NSLayoutConstraint常数,仅仅影响第一个item的NSLayoutAttribute
// 是下面NSLayoutAttributeWidth, NSLayoutAttributeHeight, 
// 之一的MASConstraints
- (MASConstraint * (^)(CGSize offset))sizeOffset;

// 修改NSLayoutConstraint常数,仅仅影响第一个item的NSLayoutAttribute
// 是下面NSLayoutAttributeCenterX, NSLayoutAttributeCenterY, 
// 之一的MASConstraints
- (MASConstraint * (^)(CGPoint offset))centerOffset;

// 修改NSLayoutConstraint常数
- (MASConstraint * (^)(CGFloat offset))offset;

// 基于值类型,修改NSLayoutConstraint常数
- (MASConstraint * (^)(NSValue *value))valueOffset;

//设置NSLayoutConstraint的multiplier属性
- (MASConstraint * (^)(CGFloat multiplier))multipliedBy;

//设置NSLayoutConstraint的multiplier为1.0/dividedBy
- (MASConstraint * (^)(CGFloat divider))dividedBy;

//设置NSLayoutConstraint的优先级为一个浮点数或者MASLayoutPriority
- (MASConstraint * (^)(MASLayoutPriority priority))priority;

// 设置NSLayoutConstraint的优先级为MASLayoutPriorityLow
- (MASConstraint * (^)(void))priorityLow;

// 设置NSLayoutConstraint优先级为MASLayoutPriorityMedium
- (MASConstraint * (^)(void))priorityMedium;

//设置NSLayoutConstraint优先级为MASLayoutPriorityHigh
- (MASConstraint * (^)(void))priorityHigh;

//设置约束关系为NSLayoutRelationEqual,返回一个block,
//接收一下类型参数MASViewAttribute, UIView, NSValue, NSArray
- (MASConstraint * (^)(id attr))equalTo;

//设置约束关系为NSLayoutRelationGreaterThanOrEqual,返回一个block,
//接收一下类型参数MASViewAttribute, UIView, NSValue, NSArray
- (MASConstraint * (^)(id attr))greaterThanOrEqualTo;

//设置约束关系为NSLayoutRelationLessThanOrEqual,返回一个block,
//接收一下类型参数MASViewAttribute, UIView, NSValue, NSArray
- (MASConstraint * (^)(id attr))lessThanOrEqualTo;


// 可选的语义属性,它不起作用,但提高了约束的可读性
- (MASConstraint *)with;

// 可选的语义属性,它不起作用,但提高了约束的可读性
- (MASConstraint *)and;

// 根据调用的属性创建一个新的MASCompositeConstraint
- (MASConstraint *)left;
- (MASConstraint *)top;
- (MASConstraint *)right;
- (MASConstraint *)bottom;
- (MASConstraint *)leading;
- (MASConstraint *)trailing;
- (MASConstraint *)width;
- (MASConstraint *)height;
- (MASConstraint *)centerX;
- (MASConstraint *)centerY;
- (MASConstraint *)baseline;

#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)

- (MASConstraint *)firstBaseline;
- (MASConstraint *)lastBaseline;

#endif

#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000)

- (MASConstraint *)leftMargin;
- (MASConstraint *)rightMargin;
- (MASConstraint *)topMargin;
- (MASConstraint *)bottomMargin;
- (MASConstraint *)leadingMargin;
- (MASConstraint *)trailingMargin;
- (MASConstraint *)centerXWithinMargins;
- (MASConstraint *)centerYWithinMargins;

#endif


//设置约束constraint的debug name
- (MASConstraint * (^)(id key))key;

// 修改NSLayoutConstraint常数,仅仅影响第一个item的NSLayoutAttribute
// 是下面NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, 
// NSLayoutAttributeRight之一的MASConstraints
- (void)setInsets:(MASEdgeInsets)insets;

// 修改NSLayoutConstraint常数,仅仅影响第一个item的NSLayoutAttribute
// 是下面NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, 
// NSLayoutAttributeRight之一的MASConstraints
- (void)setInset:(CGFloat)inset;

// 修改NSLayoutConstraint常数,仅仅影响第一个item的NSLayoutAttribute
// 是下面NSLayoutAttributeWidth, NSLayoutAttributeHeigh之一的MASConstraints
- (void)setSizeOffset:(CGSize)sizeOffset;

// 修改NSLayoutConstraint常数,仅仅影响第一个item的NSLayoutAttribute
// 是下面NSLayoutAttributeCenterX, NSLayoutAttributeCenterY之一的MASConstraints
- (void)setCenterOffset:(CGPoint)centerOffset;

//修改NSLayoutConstraint常量
- (void)setOffset:(CGFloat)offset;

#if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV)

// 是否在修改约束时通过动画代理
@property (nonatomic, copy, readonly) MASConstraint *animator;
#endif

// 如果OS支持就激活一个NSLayoutConstraint,否则就调用install
- (void)activate;

// 销毁前面安装或者激活的NSLayoutConstraint
- (void)deactivate;

//创建一个NSLayoutConstraint并将它添加到合适的view上
- (void)install;

//移除以前安装的NSLayoutConstraint
- (void)uninstall;

@end


//用于MASConstraint方法的便捷自动装箱宏

#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

@interface MASConstraint (AutoboxingSupport)

//等于
- (MASConstraint * (^)(id attr))mas_equalTo;
//大于等于
- (MASConstraint * (^)(id attr))mas_greaterThanOrEqualTo;
//小于等于
- (MASConstraint * (^)(id attr))mas_lessThanOrEqualTo;

//设置一个常量
- (MASConstraint * (^)(id offset))mas_offset;

@end

Masonry基本使用

1.frame 形式x,y,w,h都固定,父子视图约束

 [redView mas_makeConstraints:^(MASConstraintMaker *make) {
     
     //方式1:
//        make.left.mas_equalTo(10);
//        make.top.mas_equalTo(64);
//        make.width.mas_equalTo(100);
//        make.height.mas_equalTo(100);
     //方式2:
//        make.left.equalTo(@10);
//        make.top.equalTo(@64);
//        make.width.equalTo(@100);
//        make.height.equalTo(@100);
     //方式3:
     make.left.equalTo(self.view).with.offset(10);
     make.top.equalTo(self.view).with.offset(64);
     make.width.equalTo(@100);
     make.height.equalTo(@100);
 }];

2.多个子视图之间约束,红绿蓝三个子视图等高且距父视图的左右下边距个为10,顶部间距为88

 CGFloat padding = 10;
 [redView mas_makeConstraints:^(MASConstraintMaker *make) {
     //因为每个属性的get方法都返回值都为MASConstraint,从而达到链式语法的目的
     make.left.right.top.equalTo(self.view).insets(UIEdgeInsetsMake(88, padding, 0, padding));
     make.bottom.equalTo(self->greenView.mas_top).offset(-padding);
 }];
 
 [greenView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.left.right.equalTo(self.view).insets(UIEdgeInsetsMake(0, padding, 0, padding));
     make.bottom.equalTo(self->blueView.mas_top).offset(-padding);
 }];
 /**
  下面设置make.height的数组是关键,通过这个数组可以设置这三个视图高度相等。其他例如宽度之类的,也是类似的方式。
  */
 [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
      make.left.right.bottom.equalTo(self.view).insets(UIEdgeInsetsMake(0, padding, padding, padding));
     make.height.equalTo(@[self->redView,self->greenView]);
 }];
多个子视图之间约束.png

3.多个子视图之间自由布局

[redView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.left.equalTo(@10);
     make.top.equalTo(@108);
     make.width.equalTo(@100);
     make.height.equalTo(@100);
 }];
 
 [greenView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.left.equalTo(self->redView.mas_right).offset(10);
//        make.top.equalTo(self->redView);
     //方式2
     make.top.equalTo(self->redView.mas_top);
     make.width.equalTo(@200);
     make.height.equalTo(@200);
 }];
 
 [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.left.equalTo(self->redView).offset(0);
     //        make.top.equalTo(self->redView);
     //方式2
     make.top.equalTo(self->greenView.mas_bottom).offset(10);
     make.width.equalTo(self.view).offset(-20);
     make.height.equalTo(@300);
 }];
多个子视图之间自由布局.png

4.居中

  [redView mas_makeConstraints:^(MASConstraintMaker *make) {
       make.center.equalTo(self.view);
       make.width.equalTo(@300);
       make.height.equalTo(@300);
   }];
   
   [greenView mas_makeConstraints:^(MASConstraintMaker *make) {
       make.center.equalTo(self.view);
       make.width.equalTo(@200);
       make.height.equalTo(@200);
   }];
   
   [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
       make.center.equalTo(self.view);
       make.width.equalTo(@100);
       make.height.equalTo(@100);
   }];
   
   blueView.layer.cornerRadius = 50;
   blueView.clipsToBounds = YES;
居中.png

5.垂直居中,及垂直方向上的中心点位置一致

    CGFloat gap = 10;
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerY.equalTo(self.view);
        make.left.mas_equalTo(gap);
        make.right.mas_equalTo(self->greenView.mas_left).offset(-gap);
        make.height.equalTo(@100);
    }];
    
    [greenView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerY.equalTo(self.view.mas_centerY);
        make.right.mas_equalTo(self->blueView.mas_left).offset(-gap);
        make.height.equalTo(@200);
        make.width.equalTo(self->redView);
    }];
    
    [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerY.equalTo(self.view);
        make.right.mas_equalTo(self.view.mas_right).offset(-gap);
        make.height.equalTo(@300);
        make.width.equalTo(self->redView);
    }];
    
垂直居中.png

6.水平居中.水平直方向上的中心点位置一致,高度相等,宽度分别为100,200,300

    CGFloat gap = 10;
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(self.view);
        make.top.mas_equalTo(64);
        make.width.equalTo(@100);
        make.height.equalTo(@[self->greenView,self->blueView]);
    }];
    
    [greenView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(self.view.mas_centerX);
        make.top.mas_equalTo(self->redView.mas_bottom).offset(gap);
        make.width.equalTo(@200);
//        make.width.equalTo(self->redView);
    }];
    
    [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(self.view);
        make.top.mas_equalTo(self->greenView.mas_bottom).offset(gap);
        make.width.equalTo(@300);
        make.bottom.equalTo(self.view).offset(-gap);
    }];
水平居中.png

7.更新约束,只会更新同类型的约束,有时候我们需要移除一个视图,并且别的视图又与该视图有约束关系,这时候就搞更新约束

   //如果其他视图与blueView有约束关系,直接移除会产生问题
    [blueView removeFromSuperview];
    //所以要更新约束或重新设置约束,或者多设置约束,设置不同优先级,当高优先级的约束不存在时就会启用低优先级的约束
    [greenView mas_updateConstraints:^(MASConstraintMaker *make) {
        make.bottom.equalTo(self.view).offset(-10);
    }];
更新约束.png

8.重置约束,会移除之间所有的约束,所以不能只设置某一个或某几个约束,保证约束齐全,只是当前视图的约束被清除重置了,但是别的视图相对于该视图的约束并没有被清除

[redView mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(@10);
        make.top.equalTo(@100);
        make.width.equalTo(@200);
        make.height.equalTo(@200);
    }];
重置约束.png
  1. UITableView动态Cell高度,iOS8之后,UITableView动态Cell高度在不考虑性能的情况下,tableView动态Cell高度,可以采取估算高度的方式。如果通过估算高度的方式实现的话,无论是纯代码还是Interface Builder,都只需要两行代码就可以完成Cell自动高度适配。只需要设置estimatedRowHeight估算高度,设置rowHeightUITableViewAutomaticDimension高度自动计算.

    用Masonry自定义cell,关键点在于顶部视图距离contentView的顶部间距,底部视图距离contentView的底部间距,子视图之间的约束都必须不能缺少.

//Masonry布局自定义cell
#import <UIKit/UIKit.h>
#import "HJLabel.h"
NS_ASSUME_NONNULL_BEGIN

@interface HJTableViewCell : UITableViewCell
@property (nonatomic,strong) UILabel *titleL;
@property (nonatomic,strong) UIImageView *imageV;
@property (nonatomic,strong) HJLabel *contentL;
@property (nonatomic,strong) UILabel *pjL;
@end
NS_ASSUME_NONNULL_END

//-----实现文件---
#import "HJTableViewCell.h"
#import "Masonry.h"
@implementation HJTableViewCell

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        self.selectionStyle = UITableViewCellSelectionStyleNone;
        [self initviews];
        //布局
        [self setup];
    }
    return self;
}

- (void)initviews {
    self.titleL = [self initsLabel];
    self.imageV = [[UIImageView alloc]init];
    self.imageV.backgroundColor = UIColor.purpleColor;
    self.imageV.contentMode = UIViewContentModeScaleAspectFill;
    self.imageV.image = [UIImage imageNamed:@"girl"];
    self.imageV.backgroundColor = UIColor.purpleColor;
    self.contentL = [self initshjLabel];
    self.pjL = [self initsLabel];
    [self.contentView addSubview:self.titleL];
    [self.contentView addSubview:self.imageV];
    [self.contentView addSubview:self.contentL];
    [self.contentView addSubview:self.pjL];
    
}

- (UILabel *)initsLabel {
    UILabel * label = [[UILabel alloc]init];
    label.backgroundColor = UIColor.redColor;
    label.numberOfLines = 0;
    label.font = [UIFont systemFontOfSize:14];
    return label;
}

- (HJLabel *)initshjLabel {
    HJLabel * label = [[HJLabel alloc]init];
    label.verticalAlignment = VerticalAlignmentTop;
    label.backgroundColor = UIColor.redColor;
    label.numberOfLines = 0;
    label.font = [UIFont systemFontOfSize:14];
    return label;
}
//布局子控件
- (void)setup {
    CGFloat gap = 10;
    [self.titleL mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(gap);
        make.top.mas_equalTo(gap);
        //小于等于
        make.right.mas_lessThanOrEqualTo(self.imageV.mas_left).offset(-gap);
        //大于等于
        make.height.mas_greaterThanOrEqualTo(20);
    }];
    
    [self.imageV mas_makeConstraints:^(MASConstraintMaker *make) {
        make.right.mas_equalTo(-gap);
        make.top.mas_equalTo(gap);
        make.width.mas_equalTo(100);
        make.height.mas_equalTo(100);
    }];
    [self.contentL mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(gap);
        make.top.mas_equalTo(self.titleL.mas_bottom).offset(10);
        //小于等于默认间距 8
        make.right.mas_lessThanOrEqualTo(self.imageV.mas_left);
        //高度最小值80
        make.height.mas_greaterThanOrEqualTo(70);
    }];
    [self.pjL mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(gap);
        make.top.mas_equalTo(self.contentL.mas_bottom).offset(10);
        make.right.mas_lessThanOrEqualTo(self.contentView).offset(-2*gap);
        make.height.mas_greaterThanOrEqualTo(10);
        //正常布局不需要在设置bottom,因为高度和top都已经设置了,这里这么设置就是为了计算cell的高度
        make.bottom.equalTo(self.contentView).offset(-10);
    }];
}

tableView相关设置

- (void)setup {
    self.tableView.estimatedRowHeight = 140;
    //ios8y以后设置自动计算高度
    self.tableView.rowHeight = UITableViewAutomaticDimension;
    [self.tableView registerClass:[HJTableViewCell class] forCellReuseIdentifier:@"HJTableViewCell"];
    self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    HJTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"HJTableViewCell" forIndexPath:indexPath];
    cell.titleL.text = @"text";
    if (indexPath.row % 2 == 0) {
        cell.contentL.text = @"上海车团网络有限公司";
    }else{
        cell.contentL.text = @"上海车团网络有限公司上海车团网络有限公司上海车团网络有限公司上海车团网络有限公司上海车团网络有限公司上海车团网络有限公司上海车团网络有限公司上海车团网络有限公司上海车团网络有限公司上海车团网络有限公司上海车团网络有限公司上海车团网络有限公司";
    }

    cell.pjL.text = @"666";
    return cell;
}
UITableView动态高度cell.png

注意 : 有时候我们在tableview第一次加载时候会出现显示不正确的情况,可以再viewDidAppear方法中刷新tableview来解决

- (void)viewDidAppear:(BOOL)animated {
   [super viewDidAppear:animated];
   [self.tableView reloadData];
}
  1. UIScrollView自动布局,其实跟上面cell是一样的,当内部视图的X,Y,W,H都确定了,然后就可以通过对scrollView的上下左右约束一下就可得出其contentSize大小,有两种方式,一种是直接添加到UIScrollView上,另一种是先给UIScrollView添加一个containerView作为contentView,再将视图都添加到containerView上.

    10.1.方式1:视图直接添加到UIScrollView

     - (void)setup {
    [self.scrollV addSubview:self.redView];
    [self.scrollV addSubview:self.blueView];
    [self.scrollV addSubview:self.greenView];
    [self.scrollV addSubview:self.label];
    
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(click)];
    [self.scrollV addGestureRecognizer:tap];
    
    [self.scrollV mas_makeConstraints:^(MASConstraintMaker *make) {
        //设置内边距
        make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(88, 10, 33, 10));
    }];
    CGFloat gap = 10;
    [self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.equalTo(self.scrollV).offset(gap);
        make.width.height.mas_equalTo(300);
    }];
    
    [self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.scrollV).offset(gap);
        make.top.equalTo(self.redView.mas_bottom).offset(gap);
        make.width.height.equalTo(self.redView);
    }];
    
    [self.greenView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.scrollV).offset(gap);
        make.top.equalTo(self.blueView.mas_bottom).offset(gap);
        make.width.height.equalTo(self.redView);
    }];
    
    [self.label mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.scrollV).offset(gap);
        make.top.equalTo(self.greenView.mas_bottom).offset(gap);
        //设置最大宽度
        make.width.lessThanOrEqualTo(self.redView);
        //设置宽度
        //make.width.equalTo(self.redView);
        //设置最小高度,不设置label高度.则label高度默认为0,根据文字多少和自身宽度来自动计算自身高度
        //make.height.greaterThanOrEqualTo(@10);
        //下面这句约束很重要,一般情况下不用设置,约束条件也满足自动布局,但是这里是UIScrollView,多设置该约束是为了计算出去contentSize,从而达到UIScrollView的宽高自适应.
        make.right.bottom.equalTo(self.scrollV).offset(-gap);
    }];
    }
    
    - (void)click {
    NSString *str = @"你大爷还是你大爷!";
    static NSInteger i = 1;
    NSMutableString * resultStr = [NSMutableString string];
    for (NSInteger j = 0; j<i; j++) {
        [resultStr appendString:str];
    }
    i++;
    self.label.text = resultStr;
    }
    
    UIScrollView自动布局方式1.gif

    10.2.方式1:先给UIScrollView添加一个containerView作为contentView,再将视图都添加到containerView上.

- (void)setup2 {
[self.scrollV addSubview:self.containerView];
[self.containerView addSubview:self.redView];
[self.containerView addSubview:self.blueView];
[self.containerView addSubview:self.greenView];
[self.containerView addSubview:self.label];

[self.scrollV mas_makeConstraints:^(MASConstraintMaker *make) {
   //设置内边距
   make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(88, 10, 33, 10));
}];

[self.containerView mas_makeConstraints:^(MASConstraintMaker *make) {
   make.edges.equalTo(self.scrollV);
}];
CGFloat gap = 10;
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
   make.top.left.equalTo(self.containerView).offset(gap);
   make.bottom.equalTo(self.blueView.mas_top).offset(-gap);
   make.width.equalTo(@300);
   make.height.equalTo(@300);
}];
[self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {
   make.left.equalTo(self.containerView).offset(gap);
   make.bottom.equalTo(self.greenView.mas_top).offset(-gap);
   make.size.equalTo(self.redView);
}];
[self.greenView mas_makeConstraints:^(MASConstraintMaker *make) {
   make.left.equalTo(self.containerView).offset(gap);
   make.size.equalTo(self.redView);
   make.bottom.equalTo(self.label.mas_top).offset(-gap);
}];

[self.label mas_makeConstraints:^(MASConstraintMaker *make) {
   make.left.equalTo(self.containerView).offset(gap);
   make.width.equalTo(self.redView);
   make.height.greaterThanOrEqualTo(self.redView);
    //这个就是为了约束self.containerView的宽高,而上面又添加了containerView距scrollV的内边距,从而就可以计算出contentSize,从而达到UIScrollView的宽高自适应.
   make.bottom.right.equalTo(self.containerView).offset(-gap);
}];
}
UIScrollView自动布局方式2.gif
由上面可以看出不管是方式1还是方式2,实现UIScrollView自动布局最关键的代码为` make.right.bottom.equalTo(self.scrollV).offset(-gap);`,`make.bottom.right.equalTo(self.containerView).offset(-gap);`,自内部子视图的约束已经完全满足的情况下,多设置该约束是为了计算出去contentSize,从而达到UIScrollView的宽高自适应.

Masonry结构与源码简单解析

`MASConstraintMaker`的`install`方法是为了安装约束
```objectivec
- (NSArray *)install {
//如果标记了已经存在的约束,就先移除
if (self.removeExisting) {
    NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
    for (MASConstraint *constraint in installedConstraints) {
        [constraint uninstall];
    }
}
NSArray *constraints = self.constraints.copy;
//变了约束MASConstraint类型
for (MASConstraint *constraint in constraints) {
    constraint.updateExisting = self.updateExisting;
    //安装
    [constraint install];
}
[self.constraints removeAllObjects];
return constraints;
 } 
```

可以看出真正的安装操作是调用`MASConstraint`的`install`方法,但是`MASConstraint`中并没有实现该方法,而最终都是在其子类`MASViewConstraint`中实现:
```objectivec
- (void)install {
if (self.hasBeenInstalled) {
    return;
}

if ([self supportsActiveProperty] && self.layoutConstraint) {
    self.layoutConstraint.active = YES;
    [self.firstViewAttribute.view.mas_installedConstraints addObject:self];
    return;
}

MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item;
NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;
MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item;
NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute;

// alignment attributes must have a secondViewAttribute
// therefore we assume that is refering to superview
// eg make.left.equalTo(@10)
if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {
    secondLayoutItem = self.firstViewAttribute.view.superview;
    secondLayoutAttribute = firstLayoutAttribute;
}

MASLayoutConstraint *layoutConstraint
    = [MASLayoutConstraint constraintWithItem:firstLayoutItem
                                    attribute:firstLayoutAttribute
                                    relatedBy:self.layoutRelation
                                       toItem:secondLayoutItem
                                    attribute:secondLayoutAttribute
                                   multiplier:self.layoutMultiplier
                                     constant:self.layoutConstant];

layoutConstraint.priority = self.layoutPriority;
layoutConstraint.mas_key = self.mas_key;

if (self.secondViewAttribute.view) {
    MAS_VIEW *closestCommonSuperview = [self.firstViewAttribute.view mas_closestCommonSuperview:self.secondViewAttribute.view];
    NSAssert(closestCommonSuperview,
             @"couldn't find a common superview for %@ and %@",
             self.firstViewAttribute.view, self.secondViewAttribute.view);
    self.installedView = closestCommonSuperview;
} else if (self.firstViewAttribute.isSizeAttribute) {
    self.installedView = self.firstViewAttribute.view;
} else {
    self.installedView = self.firstViewAttribute.view.superview;
}


MASLayoutConstraint *existingConstraint = nil;
if (self.updateExisting) {
    existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];
}
if (existingConstraint) {
    // just update the constant
    existingConstraint.constant = layoutConstraint.constant;
    self.layoutConstraint = existingConstraint;
} else {
    [self.installedView addConstraint:layoutConstraint];
    self.layoutConstraint = layoutConstraint;
    [firstLayoutItem.mas_installedConstraints addObject:self];
}
}
```

Masonry使用技巧与注意事项


先关参考:

上一篇 下一篇

猜你喜欢

热点阅读