Masonry源码分析

以一段代码为例

导入头文件 #import "Masonry.h"

  [self.view addSubview:view1];
  [view1 mas_makeConstraints:^(MASConstraintMaker *make) {            make.topMargin.equalTo(self.view.mas_top).offset(100);    
   make.leftMargin.equalTo(self.view.mas_left).offset(100);
        make.width.mas_equalTo(100);
        make.height.mas_equalTo(100);

    }];

这段代码很简单新建一个子控件View1. 添加到self.view身上, 设置约束.
1 顶部距离self.view顶部100
2 左边距离self.view左边100
3 宽等于100,
4 高等于100,


从代码的角度分析
[view1 mas_makeConstraints:^(MASConstraintMaker *make){.....}
调用的方法在 View+MASAdditions 文件下.
源码如下:

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    // 取消autolayout
    self.translatesAutoresizingMaskIntoConstraints = NO; 
    // 实例化 make 
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    // 调用block 设置约束
    block(constraintMaker);
   // 把约束 设置给系统
    return [constraintMaker install];
}

关注一下第二行代码
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
留意一下 这里把self 传入了, 这是一个UIView的分类 所以self就是你当前要设置约束的空间, 在这个例子中就是view1, 传入之后会保存在 一个叫view的成员变量里

重点是block(constraintMaker); 这行代码的调用.
1. 他调用了block, 示例代码中的这段代码会执行.

make.topMargin.equalTo(self.view.mas_top).offset(100);      make.leftMargin.equalTo(self.view.mas_left).offset(100);
        make.width.mas_equalTo(100);
        make.height.mas_equalTo(100);

2 他将上一步实例化好的 MASConstraintMaker 实例返回, 也就是示例代码中的 make.


在来分析一下这段代码
make.topMargin.equalTo(self.view.mas_top).offset(100);

make 就是上文中说的 MASConstraintMaker 的一个实例对象. 来看看 make.topMargin 做了什么

- (MASConstraint *)topMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTopMargin];
}

- (MASConstraint *)bottomMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottomMargin];
}

这里为了减少篇幅, 只展示了topMargin和 bottomMargin的源码,其实还有leadingMargin, rightMargin, centerXWithinMargins 以及make.width.mas_equalTo(100) 里的 width , height bottom
都是在调用
[self addConstraintWithLayoutAttribute:...]

请注意这里会把你设置的topMargin, bottomMargin 转换成OC 认识的 NSLayoutAttributeTopMargin , NSLayoutAttributeBottomMargin 并传递下去
这个方法会继续往下调用

- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}

请注意 constraint:传递的是nil


下面来看看
[self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
这段代码做什么了
请注意 constraint:传递的是nil

- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    // 设置 _layoutAttribute = layoutAttribute
    MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
    // 设置   _firstViewAttribute = viewAttribute; layoutPriority,  layoutMultiplier
    MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
    if ([constraint isKindOfClass:MASViewConstraint.class]) {
        //replace with composite constraint
        NSArray *children = @[constraint, newConstraint];
        MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
        compositeConstraint.delegate = self;
        [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
        return compositeConstraint;
    }
    if (!constraint) {
        newConstraint.delegate = self;
        [self.constraints addObject:newConstraint];
    }
    return newConstraint;
}

因为 constraint 是nil, 先不管它, 我们先来看看第二个参数 layoutAttribute 去那了.

关注一下 这下面这两行代码

 // 设置 _layoutAttribute = layoutAttribute
    MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
    // 设置   _firstViewAttribute = viewAttribute; layoutPriority,  layoutMultiplier
    MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];

就如注释所示: layoutAttribute先给了 第一句的 viewAttribute, 并在内部复制给了 _layoutAttribute 的成员变量. self.view 就是上文中提到的 self传入后 保存self的变量.
第二句 : 用 viewAttribute 实例化了 newConstraint 对象, 并且在内部, 将viewAttribute 复制给了 _firstViewAttribute 的成员变量.
留意一下 _firstViewAttribute 一会有彩蛋.

好了这两句分析完了, 因为constraint 是nil,暂时不分析有值的情况. 接着往下看

if (!constraint) {
   newConstraint.delegate = self;
   [self.constraints addObject:newConstraint];
 }
  return newConstraint;
  1. newConstraint 添加到 数组中. 可以保证 它不会被释放.
  2. 将newConstraint返回. 注意是MASViewConstraint 类型的实例对象. 这样就开始了 链式编程.

小结一下
make.topMargin 会实例化一个 MASViewConstraint 类型的 变量 newConstraint . 并且将newConstraint 放到数组中, 保证他不会被释放.
现在 make.topMargin 就可以看做 newConstraint


在看 equalTo(self.view.mas_top) 这个可以看做 newConstraint.equalTo(self.view.mas_top)
让我们看看这段代码 做了什么

- (MASConstraint * (^)(id))equalTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}

很明显 返回了一个block. 需要传递一个参数.
newConstraint.equalTo(self.view.mas_top) 就是在调用这个block. 并且将 self.view.mas_top 传递进去.

关注一下
self.equalToWithRelation(attribute, NSLayoutRelationEqual)

很明显 这也是一个block.

请注意第一个参数 attribute 就是 传入的 self.view.mas_top 第二个参数 NSLayoutRelationEqual 这个是表示关系是 等于.

- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation {
    return ^id(id attribute, NSLayoutRelation relation) {
        if ([attribute isKindOfClass:NSArray.class]) {
            NSAssert(!self.hasLayoutRelation, @"Redefinition of constraint relation");
            NSMutableArray *children = NSMutableArray.new;
            for (id attr in attribute) {
                MASViewConstraint *viewConstraint = [self copy];
                viewConstraint.layoutRelation = relation;
                viewConstraint.secondViewAttribute = attr;
                [children addObject:viewConstraint];
            }
            MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
            compositeConstraint.delegate = self.delegate;
            [self.delegate constraint:self shouldBeReplacedWithConstraint:compositeConstraint];
            return compositeConstraint;
        } else {
            NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @"Redefinition of constraint relation");
            self.layoutRelation = relation;
            self.secondViewAttribute = attribute;
            return self;
        }
    };

我们先忽略 attribute 是数组的情况, 那这段代码需要关心的就剩

  self.layoutRelation = relation;
  self.secondViewAttribute = attribute;
  return self;

第一行 记录的关系,
第二行 记录了 self.view.mas_top
第三行 返回self 继续链式编程.


在看最后一个操作 offset(100) 因为上一个语句返回的事self 所以依旧可以看成 newConstraint.offset(100)

offset(100) 很明显 这也是一个block
而这个block很简单

- (MASConstraint * (^)(CGFloat))offset {
    return ^id(CGFloat offset){
        self.offset = offset;
        return self;
    };
}

只是记录了一下数据.
到此 一个约束描述完毕.
回顾一下.
make.topMargin
1 将topMargin 转换成oc认识的 NSLayoutAttributeTopMargin
2 根据self.view (就是你传入self时保存self的属性) 和 NSLayoutAttributeTopMargin 最终实例化 MASViewConstraint 类型对象 newConstraint.
3 将newConstraint 返回做链式编程.

equalTo(self.view.mas_bottom)
调用block 将equalTo 转换成oc认识的 NSLayoutRelationEqual 并使用
self.layoutRelation = relation;
self.secondViewAttribute = attribute;

记录

offset(-100)
就简单记录了一下数据 self.offset = offset;
到此 newConstraint 里就保存了 设置一条约束的所有条件.

其他的约束也大概是这样一个逻辑
当所有约束设置完后, 就 调用 [constraintMaker install]


- (void)install 方法太长 我们捡重点的说

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

MASLayoutConstraint 继承的是NSLayoutConstraint 这个方法就是在调用NSLayoutConstraint 的方法. 将一条约束设置给系统.

还记得上文留的彩蛋吗? 就是这里的 firstLayoutAttribute.

未完 待续

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值