以一段代码为例
导入头文件 #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;
- 把
newConstraint
添加到 数组中. 可以保证 它不会被释放. - 将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.
未完 待续