Masonry 结构图,本文会按照程序的执行来讲解。 本文主要分两部分,第一部分是约束的安装,主要看约束的整个操作过程;第二部分分析Block代码块中的操作 (下图是由作者青玉伏案 所做,作者做的很漂亮这里拿来参考一下)。

第一部分 约束的安装部分
1. View+MASAdditions categary方法入口
1.1 方法入口一般如下,我们从头开始分析:
//添加约束
- (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;
1.2 首先查看添加约束的方法,方法如下:
其实这段代码很简单,就是创建一个约束制造者,将block中的约束添加后更新约束,更新约束和重新添加约束代码类似,比第一种方式添加了updateExisting和removeExisting的设置
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
//translatesAutoresizingMaskIntoConstraints可以自动将frame转化为约束,设置约束时需要关闭
self.translatesAutoresizingMaskIntoConstraints = NO;
//约束制造者 --- 初始化,并且初始化约束的数组
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
//设置block的约束 --- 回到block的操作
block(constraintMaker);
//升级
return [constraintMaker install];
}
1.3 这一段代码暂时跳过,下文讲到MASViewConstraint类的install方法时回来查看就可以更快的明白作者的意图
- (instancetype)mas_closestCommonSuperview:(MAS_VIEW *)view {
//获取两个视图最近的公共父视图 -----> 就是说一层一层的父视图找,直到两个视图的父视图是一个的时候 跳出循环
//目的是为了给两个view添加约束时找到他的父视图
MAS_VIEW *closestCommonSuperview = nil;
MAS_VIEW *secondViewSuperview = view;
while (!closestCommonSuperview && secondViewSuperview) {
MAS_VIEW *firstViewSuperview = self;
while (!closestCommonSuperview && firstViewSuperview) {
if (secondViewSuperview == firstViewSuperview) {
closestCommonSuperview = secondViewSuperview;
}
firstViewSuperview = firstViewSuperview.superview;
}
secondViewSuperview = secondViewSuperview.superview;
}
return closestCommonSuperview;
}
2. MASConstraintMaker 约束管理者
上面在分类中调用了MASConstraintMaker中的两个方法,初始化操作和install操作,代码如下
2.1 约束管理者初始化,主要是初始化一个存储约束的数组
- (id)initWithView:(MAS_VIEW *)view {
self = [super init];
if (!self) return nil;
//当前视图
self.view = view;
//存储约束的数组
self.constraints = NSMutableArray.new;
return self;
}
2.2 install操作
- (NSArray *)install {
//这里判断是否是重新添加约束
if (self.removeExisting) {
//获取view中存在的约束
NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
//获取到的数组挨个调用卸载方法,删除约束
for (MASConstraint *constraint in installedConstraints) {
[constraint uninstall];
}
}
//self.constraints将block中添加的约束进行遍历
NSArray *constraints = self.constraints.copy;
for (MASConstraint *constraint in constraints) {
//设置约束的updateExisting,如果调用的是更新方法在此处进行更新
constraint.updateExisting = self.updateExisting;
//将约束安装
[constraint install];
}
//安装完成移除所有的约束
[self.constraints removeAllObjects];
return constraints;
}
3. MASViewConstraint 具体的约束
3.1 原生类的操作
为了理清这个类的作用,我们先看一下苹果为我们提供的原生操作(如下),可以猜测这个类主要的操作就是下面重点标记的那一步操作
UIView *superview = self.view;
UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[superview addConstraints:@[
//重点看这一步的操作
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top]
]];
3.2 installedConstraintsForView方法
这里给view动态添加了一个mas_installedConstraints的get方法,里面存储了install中添加的方法,下面的方法会往这个数组中添加MASViewConstraint属性
+ (NSArray *)installedConstraintsForView:(MAS_VIEW *)view {
return [view.mas_installedConstraints allObjects];
}
3.3 install中的操作
3.3.1 我们先看下install方法中的操作
方法比较长,先看其中最重要的方法,感觉和苹果原生的使用很像,进入MASLayoutConstraint的头文件发现 @interface MASLayoutConstraint : NSLayoutConstraint,其实就是在这将约束添加到视图上的
//生成一个NSLayoutConstraint
MASLayoutConstraint *layoutConstraint
= [MASLayoutConstraint constraintWithItem:firstLayoutItem
attribute:firstLayoutAttribute
relatedBy:self.layoutRelation
toItem:secondLayoutItem
attribute:secondLayoutAttribute
multiplier:self.layoutMultiplier
constant:self.layoutConstant];
3.3.2 我们来详细分析这段代码每一句的意思
暂时忽略,代码中讲到mas_closestCommonSuperview方法可以在这进行跳转
- (void)install {
//判断约束是否存在 不存在就直接返回
if (self.hasBeenInstalled) {
return;
}
//[self supportsActiveProperty]判断是否能响应
if ([self supportsActiveProperty] && self.layoutConstraint) {
//让约束起作用
self.layoutConstraint.active = YES;
//[self.firstViewAttribute.view.mas_installedConstraints addObject:self];
return;
}
//获取父视图和当前视图及对应的约束 这里的Attribute是视图和约束的一个封装类
MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item;
NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;
MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item;
NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute;
//如果secondViewAttribute不存在并且firstViewAttribute约束不是宽高,那么就把view的父视图及约束设置给secondViewAttribute
if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {
secondLayoutItem = self.firstViewAttribute.view.superview;
secondLayoutAttribute = firstLayoutAttribute;
}
//生成一个NSLayoutConstraint
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;
//得到两个视图当前的公共superview
if (self.secondViewAttribute.view) {
/*******************************************
/ mas_closestCommonSuperview 这个方法具体操作可以回到View+MASAdditions类中查看就是上文告诉大家暂时跳过的地方
*/
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;
}
//如果是更新的话,判断具体更新的参数是什么 可到layoutConstraintSimilarTo中查看
MASLayoutConstraint *existingConstraint = nil;
if (self.updateExisting) {
existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];
}
if (existingConstraint) {
// 如果只是更新的话,更新一下这个约束
existingConstraint.constant = layoutConstraint.constant;
self.layoutConstraint = existingConstraint;
} else {
//添加layout到父视图,并且把约束类添加到mas_installedConstraints中
[self.installedView addConstraint:layoutConstraint];
self.layoutConstraint = layoutConstraint;
[firstLayoutItem.mas_installedConstraints addObject:self];
}
}
3.4 uninstall中的操作
看完了上面的代码就可以松一口气了,下面的uninstall过程就很简单了
- (void)uninstall {
//判断能否响应active
if ([self supportsActiveProperty]) {
//让约束不起作用
self.layoutConstraint.active = NO;
//从数组里删除掉这个约束
[self.firstViewAttribute.view.mas_installedConstraints removeObject:self];
return;
}
//从父视图删除约束
[self.installedView removeConstraint:self.layoutConstraint];
self.layoutConstraint = nil;
self.installedView = nil;
//删除约束
[self.firstViewAttribute.view.mas_installedConstraints removeObject:self];
}
第二部分 Block代码块操作
这部分操作讲述之前先上一张图,不知道大家有没有和我相同的疑问,那就是left和right调用颜色为什么不同,看完这部分大家就会有自己的答案了

感兴趣的朋友可以把下面代码粘贴到IDE试一下
[self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.and.bottom.equalTo(self);
make.top.equalTo(self.topView.mas_bottom);
make.height.equalTo(self.topView);
}];
1. make.top.equalTo(self.topView.mas_bottom)
我们先来分析标题中的代码调用的方式
1.1 maker的属性调用流程及其结果
1.1.1 调用的left/right/top/bottom等方法
left/right/top/bottom.....,调用的方法相同
- (MASConstraint *)top {
//添加不同的约束属性 返回值是约束的属性
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}
1.1.2 添加属性,并返回一个MASViewAttribute对象
添加属性到constraints数组中,第一次进入constraint为空,那么去掉第一个判断的内容,代码如下
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
//初始化一个MASViewAttribute,存放view和layoutAttribute
MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
//初始化一个MASViewConstraint
MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
if (!constraint) {
//设置代理方法
newConstraint.delegate = self;
//有的话就添加到constraints
[self.constraints addObject:newConstraint];
}
return newConstraint;
}
1.2 equalTo调用分析
1.2.1 查找equalTo具体调用
通过对equalTo的查找发现调用的是如下的代码,可以看到这部分方法中调用的代码是相同的,由于MASAttribute是一个抽象类,在类中没有找到其实现方法,通过上一节返回类型MASViewAttribute查找其具体实现
- (MASConstraint * (^)(id))equalTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
};
}
- (MASConstraint * (^)(id))mas_equalTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
};
}
- (MASConstraint * (^)(id))greaterThanOrEqualTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationGreaterThanOrEqual);
};
}
- (MASConstraint * (^)(id))mas_greaterThanOrEqualTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationGreaterThanOrEqual);
};
}
- (MASConstraint * (^)(id))lessThanOrEqualTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationLessThanOrEqual);
};
}
- (MASConstraint * (^)(id))mas_lessThanOrEqualTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationLessThanOrEqual);
};
}
1.2.2 equalToWithRelation调用
- (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;
//把maker的constraints数组中的单个约束MASViewConstraint替换成MASCompositeConstraint这种组合约束
[self.delegate constraint:self shouldBeReplacedWithConstraint:compositeConstraint];
return compositeConstraint;
} else {
NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @"Redefinition of constraint relation");
//如果是NSValue直接给MASViewConstraint赋值
self.layoutRelation = relation;
self.secondViewAttribute = attribute;
return self;
}
};
}
2. make.left.right.and.bottom.equalTo(self)
我们先来分析标题中的代码调用的方式,区别于第一种,这种属于组合形式
2.1 类MASCompositeConstraint介绍
MASCompositeConstraint,一个组合约束类,集成自抽象类MASConstraint,其属性childConstraints数组包含了其组合的MASViewConstraint约束类,也就是说当一个block代码块中一行添加的约束超过一个以后,masonry内部会将约束统一更新为MASCompositeConstraint类,并在最后将MASCompositeConstraint实例添加到maker的constraints中
2.2 当约束超过一个时的调用
约束为一个的时候与上一小节代码执行顺序没有区别,也就是说执行make.left的时候与上文的调用方式相同,但是当调用到right的时候,程序的调用方式发生改变,改变如下(大家可以对照源码查看)
//调用.right首先调用下面代码
#import "MASConstraint.h"
- (MASConstraint *)right {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight];
}
//接着会走子类方法
#import "MASViewConstraint.h"
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");
//可以看到此时的constraint已经赋值了,delegate指向maker类
return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
}
//调用maker类中的代理方法
#import "MASConstraintMaker.h"
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
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;
//把maker的constraints数组中的单个约束MASViewConstraint替换成MASCompositeConstraint这种组合约束
[self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
return compositeConstraint;
}
//这部分就不会执行了
if (!constraint) {
newConstraint.delegate = self;
//有的话就添加到constraints
[self.constraints addObject:newConstraint];
}
return newConstraint;
}
至此masonry的分析就完成了,剩下的就是一次次的重复调用了
结语
masonry的链式语法很漂亮,有时间会给大家分析一下