Masonry 是一个轻量级的用于自动布局(AutoLayout)的第三方框架,以其简洁的使用方式,受到广大开发者的青睐。本篇文章将带你一步步的去了解其实现原理,知其所以然!
结构概览

-
最上面的几个
category
,包含了我们常用的一些方法及属性,例如:- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
-
中间的是一个继承自
NSObject
的工厂类,主要负责创建MASConstraint
对象以及把约束添加到视图上。 -
最下面
MASConstraint
是个抽象类,其中有很多的方法都必须在子类中重写。MASViewConstraint
和MASCompositeConstraint
是它的两个子类,介绍这两个之前我们先说下MASViewAttribute
:我们都知道系统创建一条约束的方法:
+(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;
MASViewAttribute
就是对attribute
和Item
这两个属性的封装;MASViewConstraint
就是对MASViewAttribute
的封装,可以理解为一条约束对象;MASCompositeConstraint
则就是约束的集合,它里面有个私有的数组用来存放多个MASViewAttribute
对象。
源码分析
View+MASAdditions
我们绘制一个居于父视图(self)上、左为 20.0f
,右为 -20.0f
并且高度一半的 view
的约束大概是这样的:
[view mas_makeConstraints:^(MASConstraintMaker *make) { make.height.equalTo(self).multipliedBy(0.5); make.top.equalTo(self).offset(20.0f); make.left.equalTo(@20.0f); make.right.offset(-20.0f);}];
我们点进 View+MASAdditions.m
里面可以看到内部:
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block { self.translatesAutoresizingMaskIntoConstraints = NO; MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; block(constraintMaker); return [constraintMaker install];}
- 首先这里已经帮我们把
translatesAutoresizingMaskIntoConstraints
属性设置为NO
了,这样我们在外面可以省去这一步。 - 然后初始化
MASConstraintMaker
工厂实例对象并保存了当前视图self.view
。 - 接着把初始化好的
MASConstraintMaker
对象传入block
,回调给外面配置约束属性。 - 最后调用
install
方法,把配置好的约束添加到视图上去。
以上就是添加约束的大概流程,我们再看看更新和重新构建约束的方法,也就是:
- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *))block { self.translatesAutoresizingMaskIntoConstraints = NO; MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; constraintMaker.updateExisting = YES; block(constraintMaker); return [constraintMaker install];}- (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block { self.translatesAutoresizingMaskIntoConstraints = NO; MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; constraintMaker.removeExisting = YES; block(constraintMaker); return [constraintMaker install];}
我们可以发现它们和 mas_makeConstraints
唯一的区别在于多传了 updateExisting
以及 removeExisting
这两个 BOOL
属性值:
mas_updateConstraints
:找到需要更新的NSLayoutConstraint
,替换成新约束。mas_remakeConstraints
:清除所有NSLayoutConstraint
,再添加新约束。
MASConstraintMaker
知道了这三个方法的大概作用和关系,我们来详细看看 MASConstraintMaker
这个工厂类是如何配置约束的:
make.height
调用链如下:
- (MASConstraint *)height { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeHeight];}- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];}- (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]) { ··· } if (!constraint) { newConstraint.delegate = self; [self.constraints addObject:newConstraint]; } return newConstraint;}
由于 constraint
传的是 nil
,所以我们先忽略中间一段代码:
- 这里先是初始化了
MASViewAttribute
对象并保存了view
、item
以及NSLayoutAttribute
三个属性。
- (id)initWithView:(MAS_VIEW *)view layoutAttribute:(NSLayoutAttribute)layoutAttribute { self = [self initWithView:view item:view layoutAttribute:layoutAttribute]; return self;}- (id)initWithView:(MAS_VIEW *)view item:(id)item layoutAttribute:(NSLayoutAttribute)layoutAttribute { self = [super init]; if (!self) return nil; _view = view; _item = item; _layoutAttribute = layoutAttribute; return self;}
- 然后又初始化了
MASViewConstraint
对象,内部配置了些默认参数并保存了第一个约束参数MASViewAttribute
。
- (id)initWithFirstViewAttribute:(MASViewAttribute *)firstViewAttribute { self = [super init]; if (!self) return nil; _firstViewAttribute = firstViewAttribute; self.layoutPriority = MASLayoutPriorityRequired; self.layoutMultiplier = 1; return self;}
- 最后设置
MASViewConstraint
对象代理并添加到一开始准备好的self.constraints
数组中,返回。
这些工作就是在输入 make.height
进行的全部工作,它会返回一个 MASViewConstraint
对象,用于之后的继续配置。
MASViewConstraint
make.height.equalTo(self)
在 make.height
返回 MASViewConstraint
对象后,会继续在这个链式的语法中调用下一个方法来指定约束的关系。
- (MASConstraint * (^)(id attr))equalTo;- (MASConstraint * (^)(id attr))greaterThanOrEqualTo;- (MASConstraint * (^)(id attr))lessThanOrEqualTo;
文章开头说过,MASConstraint
是个抽象类,具体实现都在它的两个子类中,equalTo(self)
的调用链如下:
//MASConstraint.m- (MASConstraint * (^)(id))equalTo { return ^id(id attribute) { return self.equalToWithRelation(attribute, NSLayoutRelationEqual); };}//MASViewConstraint.m- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { return ^id(id attribute, NSLayoutRelation relation) { if ([attribute isKindOfClass:NSArray.class]) { ..... } else { ..... self.layoutRelation = relation; self.secondViewAttribute = attribute; return self; } };}
这里同样先省略部分代码,方便我们阅读:
- 首先是
self.layoutRelation
保存了约束关系且重写了set
方法,在里面用self.hasLayoutRelation
这个BOOL
标识已经有约束关系。
- (void)setLayoutRelation:(NSLayoutRelation)layoutRelation { _layoutRelation = layoutRelation; self.hasLayoutRelation = YES;}
- 然后同样是重写了
self.secondViewAttribute
的set
方法,这里会根据不同的情况做不同的操作。
- (void)setSecondViewAttribute:(id)secondViewAttribute { if ([secondViewAttribute isKindOfClass:NSValue.class]) { [self setLayoutConstantWithValue:secondViewAttribute]; } else if ([secondViewAttribute isKindOfClass:MAS_VIEW.class]) { _secondViewAttribute = [[MASViewAttribute alloc] initWithView:secondViewAttribute layoutAttribute:self.firstViewAttribute.layoutAttribute]; } else if ([secondViewAttribute isKindOfClass:MASViewAttribute.class]) { _secondViewAttribute = secondViewAttribute; } else { NSAssert(NO, @"attempting to add unsupported attribute: %@", secondViewAttribute); }}
第一种情况对应的是:
make.height.equalTo(@20.0f)
调用链如下:
//MASViewConstraint.mif ([secondViewAttribute isKindOfClass:NSValue.class]) { [self setLayoutConstantWithValue:secondViewAttribute];}//MASConstraint.m- (void)setLayoutConstantWithValue:(NSValue *)value { if ([value isKindOfClass:NSNumber.class]) { self.offset = [(NSNumber *)value doubleValue]; } else if (strcmp(value.objCType, @encode(CGPoint)) == 0) { CGPoint point; [value getValue:&point]; self.centerOffset = point; } else if (strcmp(value.objCType, @encode(CGSize)) == 0) { CGSize size; [value getValue:&size]; self.sizeOffset = size; } else if (strcmp(value.objCType, @encode(MASEdgeInsets)) == 0) { MASEdgeInsets insets; [value getValue:&insets]; self.insets = insets; } else { NSAssert(NO, @"attempting to set layout constant with unsupported value: %@", value); }}//MASViewConstraint.m- (void)setOffset:(CGFloat)offset { self.layoutConstant = offset;}//MASViewConstraint.m- (void)setLayoutConstant:(CGFloat)layoutConstant { _layoutConstant = layoutConstant;#if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV) if (self.useAnimator) { [self.layoutConstraint.animator setConstant:layoutConstant]; } else { self.layoutConstraint.constant = layoutConstant; }#else self.layoutConstraint.constant = layoutConstant;#endif}
上面到最后会有个 CGFloat
类型的 layoutConstant
属性来保存值,并且在最后调用 install
方法的时候作为 constant
参数传入。
这里只看了下传入的 NSValue
为offset
的情况,还有 centerOffset
、sizeOffset
和 insets
,也都大同小异,就不熬述了。
其实这里有一点我没明白:
直到最后调用 install
方法前,self.layoutConstraint
这个 MASLayoutConstraint
类型的属性都是 nil
,那么:
self.layoutConstraint.constant = layoutConstant;
这里的赋值又有什么意义呢?
第二种情况一般是直接传入一个视图:
make.height.equalTo(self)
这时,就会初始化一个 layoutAttribute
属性与 firstViewArribute
(第一个约束参数对象)相同的 MASViewAttribute
对象,也就是第二个约束参数对象,上面代码意思就是使视图与 self
高度相等。
第三种情况会传入一个视图的 MASViewAttribute
:
make.height.equalTo(self.height)//或者make.height.equalTo(self.mas_height)
这两种写法其实效果是一样的,都是创建并返回一个 MASViewAttribute
对象。View+MASShorthandAdditions.h
这个 category
只有个 .h
,定义了我们常用的属性和方法,但是具体实现还是调用的 View+MASAdditions
里面的方法,可以理解为去掉 mas_
命名前缀。
这里还有许多属性可以设置,比如 multipliedBy
、priority
等等,就不一一熬述了。
链式语法特性的重要一环
make.height.width.equalTo(@20);
这种同时设置多个约束属性的方式相信大家一定不陌生,认真看的人可能已经猜到了:那就是通过 delegate
的方式。
上面已经提到过,在 make.height
设置第一个约束属性时,
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute
方法中,会设置 MASViewConstraint
对象代理,其作用就是为了能够同时设置多个约束属性!我们来看看 make.height.width
中 .width
的调用链:
//MASConstraint.m- (MASConstraint *)width { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeWidth];}//MASViewConstraint.m- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation"); return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];}//MASConstraintMaker.m- (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; [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint]; return compositeConstraint; } ....}
通过上面的调用链我们可以发现,最终就是通过 delegate
的方式,调用 MASConstraintMaker
工厂类中的 constraint:addConstraintWithLayoutAttribute:
方法,这也是链式语法能链起来的原因。
我们还可以发现因为 constraint
不为 nil
,所以这次初始化并返回的不是 MASViewConstraint
对象,而是 MASCompositeConstraint
这个对象了,下面我们来看看这个类。
MASCompositeConstraint
我们先来回顾下开头是怎么介绍 MASCompositeConstraint
这个类的:“MASCompositeConstraint
是约束的集合,它里面有个私有的数组用来存放多个 MASViewAttribute 对象”。
我们接着上面的例子看:
make.height.width.equalTo(@20)
当走到 .width
时:
- (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; [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint]; return compositeConstraint; } ....}
成功的走进 if
判读里面,将 .height.wight
两条约束 MASViewConstraint
对象塞到数组里,创建 MASCompositeConstraint
对象,并且同样设置了 delegate
,最后还把 self.constraints
里面事先添加好的约束 MASViewConstraint
对象替换成了 MASCompositeConstraint
对象。
- (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint { NSUInteger index = [self.constraints indexOfObject:constraint]; NSAssert(index != NSNotFound, @"Could not find constraint %@", constraint); [self.constraints replaceObjectAtIndex:index withObject:replacementConstraint];}
我们可以点击 MASCompositeConstraint
初始化方法里看看,它内部会通过 for
循环,把数组里面的所有 MASViewConstraint
对象同样设置了 delegate
。
- (id)initWithChildren:(NSArray *)children { self = [super init]; if (!self) return nil; _childConstraints = [children mutableCopy]; for (MASConstraint *constraint in _childConstraints) { constraint.delegate = self; } return self;}
这么做的目的同时是为了能够继续链式调用,比如我们再加个 .left
:
make.height.width.left.equalTo(@20);
这时候的调用链如下:
//MASConstraint.m- (MASConstraint *)left { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];}//MASCompositeConstraint.m- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { [self constraint:self addConstraintWithLayoutAttribute:layoutAttribute]; return self;}- (MASConstraint *)constraint:(MASConstraint __unused *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { id<MASConstraintDelegate> strongDelegate = self.delegate; MASConstraint *newConstraint = [strongDelegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute]; newConstraint.delegate = self; [self.childConstraints addObject:newConstraint]; return newConstraint;}
可以发现,这里又是通过 delegate
方式,调用 MASConstraintMaker
工厂类中的:
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute]; MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute]; .... return newConstraint;}
不过这次仅仅是初始化了个 MASViewConstraint
对象就直接返回了,然后回到上个方法中添加到 MASCompositeConstraint
的私有数组 self.childConstraints
中返回备用。
equalTo(@20)
因为到.left
时,返回的是 MASCompositeConstraint
对象,到这一步的时候会有点变化,调用链如下:
//MASConstraint.m- (MASConstraint * (^)(id))equalTo { return ^id(id attribute) { return self.equalToWithRelation(attribute, NSLayoutRelationEqual); };}//MASCompositeConstraint.m- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { return ^id(id attr, NSLayoutRelation relation) { for (MASConstraint *constraint in self.childConstraints.copy) { constraint.equalToWithRelation(attr, relation); } return self; };}
可以发现,这里会循环之前准备好的私有数组 self.childConstraints
,调用 MASViewConstraint.m
的 equalToWithRelation
方法,和上面讲的一样了。
make.edges.equalTo(view)
我们再来看看这种写法,调用链如下:
//MASConstraintMaker.m- (MASConstraint *)edges { return [self addConstraintWithAttributes:MASAttributeTop | MASAttributeLeft | MASAttributeRight | MASAttributeBottom];}- (MASConstraint *)addConstraintWithAttributes:(MASAttribute)attrs { __unused MASAttribute anyAttribute = (MASAttributeLeft | MASAttributeRight | MASAttributeTop | MASAttributeBottom | MASAttributeLeading | MASAttributeTrailing | MASAttributeWidth | MASAttributeHeight | MASAttributeCenterX | MASAttributeCenterY | ...... NSMutableArray *attributes = [NSMutableArray array]; if (attrs & MASAttributeLeft) [attributes addObject:self.view.mas_left]; if (attrs & MASAttributeRight) [attributes addObject:self.view.mas_right]; if (attrs & MASAttributeTop) [attributes addObject:self.view.mas_top]; ...... NSMutableArray *children = [NSMutableArray arrayWithCapacity:attributes.count]; for (MASViewAttribute *a in attributes) { [children addObject:[[MASViewConstraint alloc] initWithFirstViewAttribute:a]]; } MASCompositeConstraint *constraint = [[MASCompositeConstraint alloc] initWithChildren:children]; constraint.delegate = self; [self.constraints addObject:constraint]; return constraint;}
代码太多省略了一部分,可以发现这段代码作用就是返回一个包含多条约束的 MASCompositeConstraint
对象,接着后面的操作也都是一样的了。
上面这种写法还可以这样:
make.edges.equalTo(UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f));
这里的 equalTo
需要注意下,它是一个宏,定义在 MASConstraint.h
中:
#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__)
我们来修改下代码:
make.edges.equalTo(MASBoxValue(UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f)));
可以发现,其实里面调用的是 MASBoxValue
这个宏,它将 C
和 Objective-C
语言中的一些基本数据结构比如说 double
CGPoint
CGSize
这些值用 NSValue
进行包装。
这里还支持直接调用 size
、center
等,具体实现都差不多,就不熬述了:
make.center.equalTo(CGPointMake(0, 50));make.size.equalTo(CGSizeMake(200, 100));
make.height.equalTo(@[redView, blueView])
我再来看看这种传数组的,在走到 .equalTo
时,最终会调用 MASViewConstraint.m
里面的 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; [self.delegate constraint:self shouldBeReplacedWithConstraint:compositeConstraint]; return compositeConstraint; } else { .... } };}
这边还是遍历数组,并且 MASViewConstraint
实现 NSCopying
协议,调用 [self copy]
会创建 MASViewConstraint
对象:
- (id)copyWithZone:(NSZone __unused *)zone { MASViewConstraint *constraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:self.firstViewAttribute]; constraint.layoutConstant = self.layoutConstant; constraint.layoutRelation = self.layoutRelation; constraint.layoutPriority = self.layoutPriority; constraint.layoutMultiplier = self.layoutMultiplier; constraint.delegate = self.delegate; return constraint;}
然后会根据传的数组里面的 Value
类型来做不同的操作,前面讲过就不熬述了:
- (void)setSecondViewAttribute:(id)secondViewAttribute { if ([secondViewAttribute isKindOfClass:NSValue.class]) { [self setLayoutConstantWithValue:secondViewAttribute]; } else if ([secondViewAttribute isKindOfClass:MAS_VIEW.class]) { _secondViewAttribute = [[MASViewAttribute alloc] initWithView:secondViewAttribute layoutAttribute:self.firstViewAttribute.layoutAttribute]; } else if ([secondViewAttribute isKindOfClass:MASViewAttribute.class]) { _secondViewAttribute = secondViewAttribute; } else { NSAssert(NO, @"attempting to add unsupported attribute: %@", secondViewAttribute); }}
最后便是生成 MASCompositeConstraint
对象,并通过 delegate
方式,调用 MASConstraintMaker
的方法,替换 self.constraints
数组里的约束:
- (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint { NSUInteger index = [self.constraints indexOfObject:constraint]; NSAssert(index != NSNotFound, @"Could not find constraint %@", constraint); [self.constraints replaceObjectAtIndex:index withObject:replacementConstraint];}
添加约束到视图
mas_makeConstraints
方法的最后会调用 [constraintMaker install]
方法来添加所有存储在 self.constraints
数组中的所有约束。
// MASConstraintMaker.m- (NSArray *)install { if (self.removeExisting) { NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view]; for (MASConstraint *constraint in installedConstraints) { [constraint uninstall]; } } NSArray *constraints = self.constraints.copy; for (MASConstraint *constraint in constraints) { constraint.updateExisting = self.updateExisting; [constraint install]; } [self.constraints removeAllObjects]; return constraints;}
如果需要重新构建约束,也就是 调用 mas_remakeConstraints:
方法,会先取出视图的所有约束,然后通过一个 for
循环,调用 uninstall
来清空所有约束:
- (void)uninstall { 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];}
如果不需要重新构建约束,会取出 self.constraints
数组中准备好的约束,通过 for
循环,调用 install
来把约束添加到视图上:
if (self.hasBeenInstalled) { return;}
如果约束以及存在并是 active
会直接返回。
if ([self supportsActiveProperty] && self.layoutConstraint) { self.layoutConstraint.active = YES; [self.firstViewAttribute.view.mas_installedConstraints addObject:self]; return;}
如果 self.layoutConstraint
响应了 isActive
方法并且不为空,会激活这条约束并添加到 mas_installedConstraints
数组中,最后返回。
MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item;NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item;NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute;
这边是获取即将用于初始化 NSLayoutConstraint
的子类 MASLayoutConstraint
的几个属性。
if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) { secondLayoutItem = self.firstViewAttribute.view.superview; secondLayoutAttribute = firstLayoutAttribute;}
这边是判断当前即将添加的约束是否是 size
类型的并且 self.secondViewAttribute
也就是约束的第二个参数是 nil
,(eg make.left.equalTo(@10)
)会自动将约束添加到约束的第一个参数视图的 superview
上。
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;
然后就会初始化 NSLayoutConstraint
的子类 MASLayoutConstraint
。
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;}
这段代码会先判断是否有约束第二个参数的视图,有的话会寻找约束第一个和第二参数视图的公共 Superview
,相当于求两个数的最小公倍数;如果不满足第一个条件,会判断约束第一个参数是否是 size
类型的,是的话直接取到它的视图;最后都不满足会直接取到约束第一个参数视图父视图。
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];}
如果需要升级当前的约束就会获取原有的约束,并替换为新的约束,这样就不需要再次为 view
安装约束。如果原来的 view
中不存在可以升级的约束,那么就会在上一步寻找到的 installedView
上面添加约束。
结束语
阅读懂源码真是一件很爽的事情,如果有什么理解的不到位的地方大家多多指正。也希望大家能够耐心的看下去,一定会有所收获的。
参考链接
http://www.cnblogs.com/ludashi/p/5591572.html
原著是一个有趣的人,若有侵权,请通知删除
还没有人抢沙发呢~