Masonry 结构图,本文会按照程序的执行来说解。 本文主要分两部分,第一部分是约束的安装,主要看约束的整个操做过程;第二部分分析Block代码块中的操做 (下图是由做者青玉伏案 所作,做者作的很漂亮这里拿来参考一下)。html
//添加约束
- (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;
复制代码
其实这段代码很简单,就是建立一个约束制造者,将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];
}
复制代码
- (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;
}
复制代码
上面在分类中调用了MASConstraintMaker中的两个方法,初始化操做和install操做,代码以下less
- (id)initWithView:(MAS_VIEW *)view {
self = [super init];
if (!self) return nil;
//当前视图
self.view = view;
//存储约束的数组
self.constraints = NSMutableArray.new;
return self;
}
复制代码
- (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;
}
复制代码
为了理清这个类的做用,咱们先看一下苹果为咱们提供的原生操做(以下),能够猜想这个类主要的操做就是下面重点标记的那一步操做ui
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]
]];
复制代码
这里给view动态添加了一个mas_installedConstraints的get方法,里面存储了install中添加的方法,下面的方法会往这个数组中添加MASViewConstraint属性spa
+ (NSArray *)installedConstraintsForView:(MAS_VIEW *)view {
return [view.mas_installedConstraints allObjects];
}
复制代码
方法比较长,先看其中最重要的方法,感受和苹果原生的使用很像,进入MASLayoutConstraint的头文件发现 @interface MASLayoutConstraint : NSLayoutConstraint,其实就是在这将约束添加到视图上的3d
//生成一个NSLayoutConstraint
MASLayoutConstraint *layoutConstraint
= [MASLayoutConstraint constraintWithItem:firstLayoutItem
attribute:firstLayoutAttribute
relatedBy:self.layoutRelation
toItem:secondLayoutItem
attribute:secondLayoutAttribute
multiplier:self.layoutMultiplier
constant:self.layoutConstant];
复制代码
暂时忽略,代码中讲到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];
}
}
复制代码
看完了上面的代码就能够松一口气了,下面的uninstall过程就很简单了code
- (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];
}
复制代码
这部分操做讲述以前先上一张图,不知道你们有没有和我相同的疑问,那就是left和right调用颜色为何不一样,看完这部分你们就会有本身的答案了cdn
感兴趣的朋友能够把下面代码粘贴到IDE试一下htm
[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);
}];
复制代码
咱们先来分析标题中的代码调用的方式
left/right/top/bottom.....,调用的方法相同
- (MASConstraint *)top {
//添加不一样的约束属性 返回值是约束的属性
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}
复制代码
添加属性到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;
}
复制代码
经过对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);
};
}
复制代码
- (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;
}
};
}
复制代码
咱们先来分析标题中的代码调用的方式,区别于第一种,这种属于组合形式
MASCompositeConstraint,一个组合约束类,集成自抽象类MASConstraint,其属性childConstraints数组包含了其组合的MASViewConstraint约束类,也就是说当一个block代码块中一行添加的约束超过一个之后,masonry内部会将约束统一更新为MASCompositeConstraint类,并在最后将MASCompositeConstraint实例添加到maker的constraints中
约束为一个的时候与上一小节代码执行顺序没有区别,也就是说执行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的链式语法很漂亮,有时间会给你们分析一下