前言
Masonry 是 Objective-C 中用于 AutoLayout (see Understanding Auto Layout
) 的通用框架,一般用来代替原本 Apple AutoLayout 的繁琐的 AutoLayout 代码。
- 添加约束之前必须将view添加到superview里
- 对于要使用Auto Layout的控件需要关闭Autoresizing
- 创建并添加约束
- 更新约束
原生方式 使用 Auto Layout
//viewTopLeft的leading与其superview的leading(左侧)对齐
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:viewTopLeft attribute:NSLayoutAttributeLeading multiplier:1 constant:-10]];
//viewTopLeft的top与其superview的top对齐
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:viewTopLeft attribute:NSLayoutAttributeTop multiplier:1 constant:-10]];
//viewTopRight的top与viewTopLeft的top对齐
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:viewTopRight attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:viewTopLeft attribute:NSLayoutAttributeTop multiplier:1 constant:0]];
//viewTopRight的leading与viewTopLeft的trailing(右侧)对齐
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:viewTopRight attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:viewTopLeft attribute:NSLayoutAttributeTrailing multiplier:1 constant:10]];
//viewTopRight的trailing与其superview的右侧对其
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:viewTopRight attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTrailing multiplier:1 constant:-10]];
//viewTopRight的宽与viewTopLeft宽相等
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:viewTopRight attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:viewTopLeft attribute:NSLayoutAttributeWidth multiplier:1 constant:0]];
//viewTopRight的高与viewTopLeft高相等
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:viewTopLeft attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:viewTopRight attribute:NSLayoutAttributeHeight multiplier:1 constant:0]];
Masonry 使用可以极大的简便这个过程
[button mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(self.view);
make.top.equalTo(self.view).with.offset(40);
make.width.equalTo(@185);
make.height.equalTo(@38);
}];
入口 (UIView+MASAdditions)
// 最常用的就是 make
- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
// 当然还可以 update 和 remake
- (NSArray *)mas_updateConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
- (NSArray *)mas_remakeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
make
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
// 自动设置 self 使用 AutoLayout, 注意, secondView 并不会自动设置
self.translatesAutoresizingMaskIntoConstraints = NO;
// 创建 MASConstraintMaker,并让 maker 弱引用 self
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
// 执行 block, 给 maker 赋值, 不判空 ???
block(constraintMaker);
// 安装 maker 里面的约束
return [constraintMaker install];
}
update
设置 update 标识值, 其他与 make 一样
- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
constraintMaker.updateExisting = YES; // 设置 update 标识值
block(constraintMaker);
return [constraintMaker install];
}
remake
设置 remake 标识值, 其他与 make 一样
- (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
constraintMaker.removeExisting = YES; // 设置 remake 标识值
block(constraintMaker);
return [constraintMaker install];
}
总结
入口方法会自动设置了 firstView 使用 autolayout, 但是并不会设置 secondView。
入口方法内并没有见到明显的创建和安装约束(NSLayoutConstraint)相关的代码,推测应该在
[constraintMaker install]
实现了这个过程所以 MASConstraintMaker 应该相当于一个Manager,可以储存所有firtView是self的 MASConstraint 容器
约束的创建与安装
@interface MASConstraintMaker () <MASConstraintDelegate>
@property (nonatomic, weak) MAS_VIEW *view;
@property (nonatomic, strong) NSMutableArray *constraints;
@end
block内创建约束
链条的第一个 Attibute (MASConstraintMaker)
maker 会创建一个新的 MASConstraint 作为约束的容器, 并将 创建出的 constraint 的 delegate 指向 maker
@property (nonatomic, strong, readonly) MASConstraint *left;
@property (nonatomic, strong, readonly) MASConstraint *top;
@property (nonatomic, strong, readonly) MASConstraint *right;
@property (nonatomic, strong, readonly) MASConstraint *bottom;
....
left/centerX/width等
- (MASConstraint *)left {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
// 设置 firstViewAttribute 的 view 和 attr
MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
// MASViewConstraint 是 MASConstraint的子类
MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
// 因为 参数传的都是 nil
if ([constraint isKindOfClass:MASViewConstraint.class]) {...}
if (!constraint) {
newConstraint.delegate = self;
// maker 上使用数组 存储了所有的数组
[self.constraints addObject:newConstraint];
}
return newConstraint;
}
center/size/edges
- (MASConstraint *)edges {
return [self addConstraintWithAttributes:MASAttributeTop | MASAttributeLeft | MASAttributeRight | MASAttributeBottom];
}
- (MASConstraint *)size {
return [self addConstraintWithAttributes:MASAttributeWidth | MASAttributeHeight];
}
- (MASConstraint *)center {
return [self addConstraintWithAttributes:MASAttributeCenterX | MASAttributeCenterY];
}
- (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;
}
链条上第二个 attribute
形如make.left.and.top.equalTo(@10);
会调用 MASConstraint 的 delegate 方法,也就是 maker 的constraint:addConstraintWithLayoutAttribute:
的方法。
在该方法内,会将原约束容器替换成能够储存一组约束的容器(MASCompositeConstraint)
@interface MASConstraint : NSObject
- (MASConstraint *)left;
- (MASConstraint *)top;
- (MASConstraint *)right;
- (MASConstraint *)bottom;
...
@end
- (MASConstraint *)left {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");
return [self.delegate constraint:self 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]) {
//replace with composite constraint
NSArray *children = @[constraint, newConstraint];
// MASCompositeConstraint 也是 MASConstraint 的子类,用来储存一组约束
MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
compositeConstraint.delegate = self;
[self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
return compositeConstraint;
}
if (!constraint) {...}
return newConstraint;
}
- (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];
}
链条上 equalTo
@interface MASConstraint : NSObject
- (MASConstraint * (^)(id attr))equalTo;
- (MASConstraint * (^)(id attr))greaterThanOrEqualTo;
- (MASConstraint * (^)(id attr))lessThanOrEqualTo;
...
@end
- (MASConstraint * (^)(id))equalTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
};
}
- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation {
return ^id(id attribute, NSLayoutRelation relation) {
// 判断数组类型 ,主要用于处理此种情况 make.height.equalTo(@[view1.mas_height, view2.mas_height]);
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];
// 依次设置 layoutRelation 和 secondViewAttribute
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 {
// 其余情况都直接存入secondViewAttribute
NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @"Redefinition of constraint relation");
// 主要是设置 layoutRelation 和 secondViewAttribute
self.layoutRelation = relation;
self.secondViewAttribute = attribute;
return self;
}
};
}
// equalTo 接受三种基本参数 分别是 MAS_VIEW (iOS中就是 UIView) 、 NSValue 和 mas_left
- (void)setSecondViewAttribute:(id)secondViewAttribute {
if ([secondViewAttribute isKindOfClass:NSValue.class]) {
// 设置 width/center/size/edges, 注意 NSNumber 也是 NSValue 的子类
[self setLayoutConstantWithValue:secondViewAttribute];
} else if ([secondViewAttribute isKindOfClass:MAS_VIEW.class]) {
// 如果是个 secondViewAttribute 是 view,那么 layoutAttribute 和 firstViewAttribute 相同
_secondViewAttribute = [[MASViewAttribute alloc] initWithView:secondViewAttribute layoutAttribute:self.firstViewAttribute.layoutAttribute];
} else if ([secondViewAttribute isKindOfClass:MASViewAttribute.class]) {
// mas_left/mas_center 啥的
_secondViewAttribute = secondViewAttribute;
} else {
NSAssert(NO, @"attempting to add unsupported attribute: %@", secondViewAttribute);
}
}
// NSValue 的处理
- (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);
}
}
mas_left
- (MASViewAttribute *)mas_left {
// 储存 secondViewItem.view 和 secondViewItem.layoutAttribute
return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeLeft];
}
链条上其他环节
with/and
用于语义的修饰,无实际作用
- (MASConstraint *)with {
return self;
}
- (MASConstraint *)and {
return self;
}
offset/priority/multipliedBy等
- (MASConstraint * (^)(CGFloat))offset {
return ^id(CGFloat offset){
self.offset = offset;
return self;
};
}
- (void)setOffset:(CGFloat)offset {
self.layoutConstant = offset;
}
创建约束时可以使用
...
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;
...
mas_equalTo/mas_greaterThanOrEqualTo/mas_lessThanOrEqualTo
mas_equalTo 可以放入基本类型的参数 比如:make.width.mas_equalTo(10);
#define mas_equalTo(...) equalTo(MASBoxValue((__VA_ARGS__)))
#define mas_greaterThanOrEqualTo(...) greaterThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_lessThanOrEqualTo(...) lessThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define MASBoxValue(value) _MASBoxValue(@encode(__typeof__((value))), (value))
static inline id _MASBoxValue(const char *type, ...) {
va_list v;
va_start(v, type);
id obj = nil;
if (strcmp(type, @encode(id)) == 0) {
id actual = va_arg(v, id);
obj = actual;
} else if (strcmp(type, @encode(CGPoint)) == 0) {
CGPoint actual = (CGPoint)va_arg(v, CGPoint);
obj = [NSValue value:&actual withObjCType:type];
} else if (strcmp(type, @encode(double)) == 0) {
double actual = (double)va_arg(v, double);
obj = [NSNumber numberWithDouble:actual];
} else {...}
va_end(v);
return obj;
}
约束的 install 过程
// MASConstraintMaker 的 install 方法
- (NSArray *)install {
// remake 相关参数
if (self.removeExisting) {
// 移除当前 View 的所有约束
NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
for (MASConstraint *constraint in installedConstraints) {
// 调用约束的 uinstall 方法
[constraint uninstall];
}
}
NSArray *constraints = self.constraints.copy;
for (MASConstraint *constraint in constraints) {
constraint.updateExisting = self.updateExisting;
// 调用约束的 install 方法
[constraint install];
}
[self.constraints removeAllObjects];
return constraints;
}
// MASCompositeConstraint 的 install 方法 ,递归地 把所有的 child 约束安装
- (void)install {
for (MASConstraint *constraint in self.childConstraints) {
constraint.updateExisting = self.updateExisting;
[constraint install];
}
}
// MASViewConstraint 的 install 方法
- (void)install {
// 如果已经安装过, 不做操作
if (self.hasBeenInstalled) {
return;
}
// iOS 8 新特性, active 参数
if ([self supportsActiveProperty] && self.layoutConstraint) {
self.layoutConstraint.active = YES;
[self.firstViewAttribute.view.mas_installedConstraints addObject:self];
return;
}
MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item;
NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;
MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item;
NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute;
// alignment attributes must have a secondViewAttribute
// therefore we assume that is refering to superview
// eg make.left.equalTo(@10)
if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {
secondLayoutItem = self.firstViewAttribute.view.superview;
secondLayoutAttribute = firstLayoutAttribute;
}
// 注意!!!在这里创建约束LayoutConstraint (MASLayoutConstraint 是 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;
if (self.secondViewAttribute.view) {
// 如果secondViewAttribute 是 一个 view, 计算 公共的 super 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) {
// 如果 firstViewAttribute 是 width height , 约束安装到 firstViewAttribute.view
self.installedView = self.firstViewAttribute.view;
} else {
// 其他情况安装到 firstViewAttribute.view.superview
self.installedView = self.firstViewAttribute.view.superview;
}
MASLayoutConstraint *existingConstraint = nil;
// update 标识符判定
if (self.updateExisting) {
existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];
}
// 如果是 update 入口来的, 并且查到约束并更新
if (existingConstraint) {
// just update the constant 注意 update 只能更新约束的 constant
existingConstraint.constant = layoutConstraint.constant;
self.layoutConstraint = existingConstraint;
} else {
// 其他情况 , update 和 make 更新
[self.installedView addConstraint:layoutConstraint];
// 设置标志位
self.layoutConstraint = layoutConstraint;
// 设置 firstLayoutItem view.mas_installedConstraints,
// 注意!这里只设置了 first view 的 install,remake 的时候也是移除了mas_installedConstraints 里的约束
[firstLayoutItem.mas_installedConstraints addObject:self];
}
}
其他
-[MASViewConstraint installedConstraintsForView]
+ (NSArray *)installedConstraintsForView:(MAS_VIEW *)view {
return [view.mas_installedConstraints allObjects];
}
-[MASViewConstraint layoutConstraintSimilarTo:]
注意 判断条件
只有 constant 不同,其他全部相同的约束才会判定为 similar
- (MASLayoutConstraint *)layoutConstraintSimilarTo:(MASLayoutConstraint *)layoutConstraint {
// check if any constraints are the same apart from the only mutable property constant
// go through constraints in reverse as we do not want to match auto-resizing or interface builder constraints
// and they are likely to be added first.
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;
}
-[UIView mas_closestCommonSuperview]
计算公共的 superview 的算法
遍历 两个 view 的 superview 链
- (instancetype)mas_closestCommonSuperview:(MAS_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;
}
-[MASConstraint uninstall]
// MASCompositeConstraint 容器递归 卸载 child 容器
- (void)uninstall {
for (MASConstraint *constraint in self.childConstraints) {
[constraint uninstall];
}
}
// MASViewConstraint
- (void)uninstall {
// 支持 active 方式话直接设置约束的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];
}
总结
make/update/remake 方法会自动设置 First View (self) 使用 AutoLayout,但是 secondView 并不会设置
remake 会移除 [self mas_make/update/remake:...] 方式添加的约束,不会移除作为 secondViewItem 的约束。
update 约束只会更新 相似约束的 constant,不会能更新其他的参数
Masonry全量支持原生约束所有的设置参数,如multiplier、priority,但是代码看起来要整洁很多,这种思路很值得借鉴
Maker 管理 MASConstraint, MASConstraint 管理 layoutConstraint
MASConstraint的两个子类,一个是复合类型,一个是基础类型,这种多态的用法很常见
view.mas_installedConstraints 储存 该 view 通过 mas_make/update/remake 方式安装的所有约束
mas_equalTo 的宏定义会使
-[MASConstraint mas_equalTo:]
永远不会触发,因为宏定义会在预处理阶段就替换成相关定义的代码