有三种方式来使用代码创建约束。
- use layout anchors
- use the NSLayoutConstraint class
- use the Visual Format Language
Masonry是对NSLayoutConstraint的封装
先来看看NSLayoutConstraint的创建:
+ (instancetype)constraintWithItem:(id)view1
attribute:(NSLayoutAttribute)attr1
relatedBy:(NSLayoutRelation)relation
toItem:(id)view2
attribute:(NSLayoutAttribute)attr2
multiplier:(CGFloat)multiplier
constant:(CGFloat)c;
我们把它分成5个部分来看:
- firstViewAttribute部分,包含view1和attr1
- layoutRelation部分,relation
- secondAttribute部分,包含view2和attr2
- layoutMultiplier部分,multiplier
- layoutConstant部分,c
NSLayoutConstraint对象应该添加到哪个view上面呢?
这里分两种情况。
- 当secondAttribute的view2存在,并且是UIView类型的对象时,约束添加到view1与view2的closestCommonSuperview(最近的公共superView)
- 当secondAttribute的view2存在,但是不是UIView类型的对象时
- 当firstViewAttribute的attr1为size属性(width或者height)时,约束添加到firstViewAttribute的view1上。
- 当firstViewAttribute的attr1不是size属性,约束添加到firstViewAttribute.view.superView上。
使用Masonry链式语法的创建约束:
//第一种
make.edge.equalTo(superView).inset(UIEdgeInsetMake(10,10,10,10));
//第二种
make.left.right.height.bottom.equalTo(superView).inset(UIEdgeInsetMake(10,10,10,10));
//第三种
make.left.equalTo(superView).offset(@10);
make.right.equalTo(superView).offset(@-10);
make.top.equalTo(superView).offset(@10);
make.bottom.equalTo(superView).offset(@-10);
上面用三种方式创建了相同的约束效果。
我们先从第三种来看Masonry的实现
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(superView).offset(@10);
}];
mas_makeConstraints为UIView的MASAdditions类别所添加的方法:
#define MAS_VIEW UIView
@interface MAS_VIEW (MASAdditions)
- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
...
@end
实现:
@implementation MAS_VIEW (MASAdditions)
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
block(constraintMaker);
return [constraintMaker install];
}
...
步骤分析:
- 创建了一个MASConstraintMaker对象,拥有对象view(弱引用)。
- 在block中配置constraintMaker对象。
- [constraintMaker install]。
install方法实现:
- (NSArray *)install {
...
NSArray *constraints = self.constraints.copy;
for (MASConstraint *constraint in constraints) {
...
[constraint install];
}
[self.constraints removeAllObjects];
return constraints;
}
是将MASConstraintMaker匿名类别中的属性@property (nonatomic, strong) NSMutableArray *constraints;遍历一遍执行install方法。constraints是MASConstraint类型的对象数组。
可以想到在block中配置constraintMaker,其本质就是在constraintMaker的constraints中添加MASConstraint对象。
现在来看看make.left.equalTo(superView).offset(@10);是如何创建MASConstraint对象并添加入constraintMaker的constraints中。
MASConstraintMaker类定义了所有的约束:
@interface MASConstraintMaker : NSObject
@property (nonatomic, strong, readonly) MASConstraint *left;
...
当使用make.left时,会执行left的getter方法:
- (MASConstraint *)left {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}
- (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;
}
可以left的getter方法执行步骤如下:
- 创建一个MASViewAttribute对象viewAttribute,view为当前view,layoutAttribute:为NSLayoutAttributeLeft。
- 创建一个MASViewConstraint对象newConstraint,firstViewAttribute为viewAttribute。
- newConstraint的代理设为self。
- 将newConstraint放入constraints中。
那其实mark.left就已经创建了MASViewConstraint对象(继承自MASConstraint),并添加入maker的constraints中。
这时候很好奇make.left后面的.equalTo(superView).offset(@10)又是做了什么?
可以看出make.left返回的是MASConstraint类型对象,所以.equalTo(superView)操作是在MASConstraint对象上的。
现在来看看MASConstraint类,类的声明如下:
@interface MASConstraint : NSObject
// Chaining Support 就是用这个类来实现链式语法滴
...
- (MASConstraint * (^)(id attr))equalTo;
- (MASConstraint * (^)(CGFloat offset))offset;
..
- (MASConstraint *)left;
- (MASConstraint *)right;
...
@end
链式语法能实现的关键之处就是返回本身,分情况说:
- 当里面的属性使用需要提供参数(如equalTo(superview),使用equalTo需要提供superView这个参数)时,定义的就是一个(MASConstraint * (^)(id attr))类型的属性,这样就实现了make.left.equalTo(superView)后返回的又是一个MASConstraint,于是就链式调用啦。
- 当里面的属性可以直接使用(如make.left.right.heigt,make.left返回MASConstarint后直接使用了.right.height..),则是直接定义成MASConstraint类型的属性,于是就链式调用啦。
MASConstraint类是提供链式语法支持的,具体的约束属性设置是通过它的子类MASViewConstraint和MASCompositeConstraint。
MASViewConstraint和MASCompositeConstraint的关系:
其中MASCompositeConstraint是用来管理MASViewConstraint集合的,如make.left.right.top.bottom其实是创建了4个MASViewConstraint,其中firstAttribute的layoutAttribute属性分别是NSLayoutAttributeLeft,NSLayoutAttributeRight,NSLayoutAttributeTop,NSLayoutAttributeBottom,然后将这4个MASViewConstraint放入MASCompositeConstraint的@property (nonatomic, strong) NSMutableArray *childConstraints;中统一管理,所以当调用make.left.right.top.bottom.equalTo(superView)时,循环childConstraints调用equalTo(superView)。
所以具体的约束属性设置(NSLayoutConstraint的创建和添加)是由MASViewConstraint完成的。
MASViewConstraint是如何工作的?
MASViewConstraint类定义如下:
@interface MASViewConstraint : MASConstraint
@property (nonatomic, strong, readonly) MASViewAttribute *firstViewAttribute;
@property (nonatomic, strong, readonly) MASViewAttribute *secondViewAttribute;
- (id)initWithFirstViewAttribute:(MASViewAttribute *)firstViewAttribute;
+ (NSArray *)installedConstraintsForView:(MAS_VIEW *)view;
@end
MASViewConstraint类需要用firstViewAttribute初始化,也就是说它一生成就拥有了firstViewAttribute。
匿名闭包中的定义:
@interface MASViewConstraint ()
@property (nonatomic, strong, readwrite) MASViewAttribute *secondViewAttribute;
@property (nonatomic, weak) MAS_VIEW *installedView;
@property (nonatomic, weak) MASLayoutConstraint *layoutConstraint;
@property (nonatomic, assign) NSLayoutRelation layoutRelation;
@property (nonatomic, assign) MASLayoutPriority layoutPriority;
@property (nonatomic, assign) CGFloat layoutMultiplier;
@property (nonatomic, assign) CGFloat layoutConstant;
@property (nonatomic, assign) BOOL hasLayoutRelation;
@property (nonatomic, strong) id mas_key;
@property (nonatomic, assign) BOOL useAnimator;
@end
看到这里就大概知道MASViewConstraint是用来干什么的了,可以使用firstViewAttribute和secondViewAttribute,layoutMultiplier,layoutPriority,layoutConstant,layoutRelation来创建一个NSLayoutConstraint,赋值给layoutConstraint,并把这个layoutConstraint添加到installedView中。
所以make.left后面的.equalTo(superView)和.offset(@10)就是对当前MASViewConstraint的layoutRelation,secondViewAttribute、layoutConstant的赋值。看下是否是这样:
//基类MASConstraint
- (MASConstraint * (^)(id))equalTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
};
}
- (MASConstraint * (^)(CGFloat))offset {
return ^id(CGFloat offset){
self.offset = offset;
return self;
};
}
//抽象方法,必须子类实现
- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { MASMethodNotImplemented(); }
//子类MASViewConstraint的实现
- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation {
return ^id(id attribute, NSLayoutRelation relation) {
if ([attribute isKindOfClass:NSArray.class]) {
...
} else {
NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @"Redefinition of constraint relation");
self.layoutRelation = relation;
self.secondViewAttribute = attribute;
return self;
}
};
}
- (void)setOffset:(CGFloat)offset {
self.layoutConstant = offset;
}
以上都是对单一的Constraint操作原理,也是最简单的操作。
我们现在从第二种来看Masonry的实现
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.top.bottom.equalTo(superView).inset(UIEdgeInsetsMake(10,10,10,10));
}];
make.left前面已经分析过,现在看看.right干了什么。
//MASConstraint中right的getter方法
- (MASConstraint *)right {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight];
}
//抽象方法,需要子类实现
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute __unused)layoutAttribute {
MASMethodNotImplemented();
}
//子类MASViewConstraint的实现
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");
return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
}
呵呵哒,交给delegate去做啦。
MASConstraintDelegate
定义:
@protocol MASConstraintDelegate
- (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint;
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute;
@end
理解代理的作用先要理解容器的概念。在Masonry里存在两种容器
-
一种是MASConstraintMaker,里面有constraints,这个容器是用来放入使用make来创建的多个约束。
[greenView makeConstraints:^(MASConstraintMaker *make) { //这里使用make写了6行,所以有6个MASConstraint在make的constraint里面 make.top.greaterThanOrEqualTo(superview).offset(padding); make.left.equalTo(superview).offset(padding); make.bottom.equalTo(blueView.top).offset(-padding); make.right.equalTo(redView.left).offset(-padding); make.width.height.equalTo(redView); make.height.equalTo(blueView).multipliedBy(0.3); }]; - 一种是MASCompositeConstraint,里面有childConstraints
[greenView makeConstraints:^(MASConstraintMaker *make) { //这里使用make写了1行,所以只有1个MASConstraint(MASCompositeConstraint)在make的constraint里面 //MASCompositeConstraint里面的childConstraints有4个MASViewConstraint,分别是: //view.top equalTo superview.top //view.right equaoTo superview.right //view.bottom equalTo superview.bottom //view.right equalTo superview.right make.top.right.bottom.left.equalTo(superview); }];
下面来看这句代码在容器中的演变过程:
make.top.right.bottom.left.equalTo(superview)
- make.top
- 生成对象A:MASViewConstraint(view.top)
- 将A的delegate设为make
- 将A放入make的constraints中,此时make.constraints = [A]
- 返回A
- make.top.right
- 生成对象B:MASViewConstraint(view.right)
- 使用A和B生成MASCompositeConstraint对象C,将A,B的delegate设为C
- 将C在make.constraints中替换A,此时make.constraints = [C],C.childConstraints = [A,B]
- 返回C
- make.top.right.bottom
- 生成对象D:MASViewConstraint(view.bottom),D的delegate为C
- 将D放入C.childConstraints中,此时C.childConstraints = [A,B,D]
- 返回C
- make.top.right.bottom.left
- 生成对象E:MASViewConstraint(view.left),E的delegate为C
- 将E放入C.childConstraints中,此时C.childConstraints = [A,B,D,E]
- 返回C
- make.top.right.bottom.left.equalTo(superview)
- 会依次调用A,B,D,E的equalTo(superView)
在上面的过程中可以看到:
- 对make.constraints的添加和替换元素的操作
- 对MASCompositeConstraint对象的添加元素的操作(当.equalTo(@[view1,view2])时就有替换操作了,在里面没体现出)。
- 每个constraint的delegate为它的父容器,因为需要父容器来执行添加和替换约束的操作。
理解上面的部分,代码就很好理解了:
基类MASConstraint的代码:
#pragma mark - Chaining
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute __unused)layoutAttribute {
MASMethodNotImplemented();
}
- (MASConstraint *)left {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}
- (MASConstraint *)top {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}
...
- (MASConstraint * (^)(id))equalTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
};
}
...
MASViewConstraint的实现
#pragma mark - attribute chaining
//添加操作
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");
return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
}
//里面有替换操作
- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation {
return ^id(id attribute, NSLayoutRelation relation) {
if ([attribute isKindOfClass:NSArray.class]) {
//对应.equalTo(@[view1,view2])这种写法
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;
//使用delegate的shouldBeReplacedWithConstraint方法
[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;
}
};
}
MASCompositeConstraint的实现
#pragma mark - attribute chaining
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
[self constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
return self;
}
#pragma mark - MASConstraintDelegate
- (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint {
NSUInteger index = [self.childConstraints indexOfObject:constraint];
NSAssert(index != NSNotFound, @"Could not find constraint %@", constraint);
[self.childConstraints replaceObjectAtIndex:index withObject:replacementConstraint];
}
- (MASConstraint *)constraint:(MASConstraint __unused *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
id strongDelegate = self.delegate;
MASConstraint *newConstraint = [strongDelegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
newConstraint.delegate = self;
[self.childConstraints addObject:newConstraint];
return newConstraint;
}
MASConstraintMaker的实现
#pragma mark - MASConstraintDelegate
- (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];
}
- (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 (!constraint) {
newConstraint.delegate = self;
[self.constraints addObject:newConstraint];
}
return newConstraint;
}
从第一种写法来看Masnory的实现
实现代码:
- (MASConstraint *)addConstraintWithAttributes:(MASAttribute)attrs {
__unused MASAttribute anyAttribute = (MASAttributeLeft | MASAttributeRight | MASAttributeTop | MASAttributeBottom | MASAttributeLeading
| MASAttributeTrailing | MASAttributeWidth | MASAttributeHeight | MASAttributeCenterX
| MASAttributeCenterY | MASAttributeBaseline
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
| MASAttributeFirstBaseline | MASAttributeLastBaseline
#endif
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000)
| MASAttributeLeftMargin | MASAttributeRightMargin | MASAttributeTopMargin | MASAttributeBottomMargin
| MASAttributeLeadingMargin | MASAttributeTrailingMargin | MASAttributeCenterXWithinMargins
| MASAttributeCenterYWithinMargins
#endif
);
NSAssert((attrs & anyAttribute) != 0, @"You didn't pass any attribute to make.attributes(...)");
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];
if (attrs & MASAttributeBottom) [attributes addObject:self.view.mas_bottom];
if (attrs & MASAttributeLeading) [attributes addObject:self.view.mas_leading];
if (attrs & MASAttributeTrailing) [attributes addObject:self.view.mas_trailing];
if (attrs & MASAttributeWidth) [attributes addObject:self.view.mas_width];
if (attrs & MASAttributeHeight) [attributes addObject:self.view.mas_height];
if (attrs & MASAttributeCenterX) [attributes addObject:self.view.mas_centerX];
if (attrs & MASAttributeCenterY) [attributes addObject:self.view.mas_centerY];
if (attrs & MASAttributeBaseline) [attributes addObject:self.view.mas_baseline];
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
if (attrs & MASAttributeFirstBaseline) [attributes addObject:self.view.mas_firstBaseline];
if (attrs & MASAttributeLastBaseline) [attributes addObject:self.view.mas_lastBaseline];
#endif
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000)
if (attrs & MASAttributeLeftMargin) [attributes addObject:self.view.mas_leftMargin];
if (attrs & MASAttributeRightMargin) [attributes addObject:self.view.mas_rightMargin];
if (attrs & MASAttributeTopMargin) [attributes addObject:self.view.mas_topMargin];
if (attrs & MASAttributeBottomMargin) [attributes addObject:self.view.mas_bottomMargin];
if (attrs & MASAttributeLeadingMargin) [attributes addObject:self.view.mas_leadingMargin];
if (attrs & MASAttributeTrailingMargin) [attributes addObject:self.view.mas_trailingMargin];
if (attrs & MASAttributeCenterXWithinMargins) [attributes addObject:self.view.mas_centerXWithinMargins];
if (attrs & MASAttributeCenterYWithinMargins) [attributes addObject:self.view.mas_centerYWithinMargins];
#endif
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;
}
很暴力通俗的代码,不用解释了...
所以make.edge就是:
- (MASConstraint *)edges {
return [self addConstraintWithAttributes:MASAttributeTop | MASAttributeLeft | MASAttributeRight | MASAttributeBottom];
}
也可以在外部使用:
@property (nonatomic, strong, readonly) MASConstraint *(^attributes)(MASAttribute attrs);
写法:
//与make.edge是相同的
make.attributes(MASAttributeTop | MASAttributeLeft | MASAttributeRight | MASAttributeBottom);
Masnory如何make,reMake,updateConstraint
直接看代码
@implementation MAS_VIEW (MASAdditions)
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
block(constraintMaker);
return [constraintMaker 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三部曲:创建maker,配置maker,install maker中,mas_updateConstraints将maker的updateExisting设为YES。mas_remakeConstraints将maker的removeExisting设为YES。
来看看updateExisting和removeExisting在install方法中如何操控的:
- (NSArray *)install {
if (self.removeExisting) {
//获得view的所有生效的约束(MASViewConstraint类型,封装了NSLayoutConstraint),然后uninstall
NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
for (MASConstraint *constraint in installedConstraints) {
[constraint uninstall];
}
}
NSArray *constraints = self.constraints.copy;
for (MASConstraint *constraint in constraints) {
//标记updateExisting为YES的时候,在constraint的install里面会去找是否有相同的约束
constraint.updateExisting = self.updateExisting;
[constraint install];
}
[self.constraints removeAllObjects];
return constraints;
}
关于MASConstraint的install,在MASCompositeConstraint中是循环childContraints的install,在MASViewContraint中是用当前的所有属性创建layoutConstraint并添加到installView上,installVIew如何确定,NSLayoutConstraint对象应该添加到哪个view上面呢?已经说了。代码很好理解,不贴了。
至于设置了updateExisting的install流程的改变:
-
找到相同的约束,何为相同,只有constant不同,其它都要相同。
-(MASLayoutConstraint *)layoutConstraintSimilarTo:(MASLayoutConstraint *)layoutConstraint { for (NSLayoutConstraint *existingConstraint in self.installedView.constraints.reverseObjectEnumerator) { if (![existingConstraint isKindOfClass:MASLayoutConstraint.class]) continue; if (existingConstraint.firstItem != layoutConstraint.firstItem) continue; if (existingConstraint.secondItem != layoutConstraint.secondItem) continue; if (existingConstraint.firstAttribute != layoutConstraint.firstAttribute) continue; if (existingConstraint.secondAttribute != layoutConstraint.secondAttribute) continue; if (existingConstraint.relation != layoutConstraint.relation) continue; if (existingConstraint.multiplier != layoutConstraint.multiplier) continue; if (existingConstraint.priority != layoutConstraint.priority) continue; return (id)existingConstraint; } return nil; } - 如果找到了就改变已经返回的constraint的constant,并把这个constraint赋值给layoutConstraint就OVER了。