Masonry源码阅读
AutoLayout是Apple在iOS6中新增的UI布局适配的方法,用来替代iOS6之前的AutoResizing。自动布局的约束可以通过拖拽的方式添加,也可以通过代码添加。AutoLayout对应的代码约束就是NSLayoutConstraint。
NSLayoutConstraint的API虽然很简单,但是添加约束的代码量却很大,因此出现了许多对NSLayoutConstraint的封装,Masonry就是其中一个。
为了使子视图填满父视图并且留10像素的间隙,来看看用官方API和Masonry添加约束的差别:
官方API:
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:@[
//view1 constraints
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:padding.left],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-padding.bottom],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1
constant:-padding.right],
]];
Masonry
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
}];
可以明显看到使用Masonry添加约束的代码精简许多。
基本使用
Msonry的使用方法很简单,主要有以下三个方法:
-
mas_remakeConstraints
会清除View已有的约束,再添加新的约束 -
mas_makeConstraints
不会清楚之前的约束,直接添加新的约束 -
mas_updateConstraints
如果该条件的约束已经存在,则直接更新,否则添加新的约束。
添加约束的时机
查看官方实例即可一目了然
@implementation DIYCustomView
- (id)init {
self = [super init];
if (!self) return nil;
// --- Create your views here ---
self.button = [[UIButton alloc] init];
return self;
}
// tell UIKit that you are using AutoLayout
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
// --- remake/update constraints here
[self.button remakeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(@(self.buttonSize.width));
make.height.equalTo(@(self.buttonSize.height));
}];
//according to apple super should be called at end of method
[super updateConstraints];
}
- (void)didTapButton:(UIButton *)button {
// --- Do your changes ie change variables that affect your layout etc ---
self.buttonSize = CGSize(200, 200);
// tell constraints they need updating
[self setNeedsUpdateConstraints];
}
@end
有几个需要注意的点:
- updateConstraints方法中添加约束要用
mas_remakeConstraints
方法 - updateConstraints方法中要在
最后
调用父类的方法 - 更新约束后要调用
[self setNeedsUpdateConstraints]
setNeedsUpdateConstraints
:当一个自定义view的某个属性发生改变,并且可能影响到constraint时,需要调用此方法去标记constraints需要在未来的某个点更新,系统然后调用updateConstraints;
updateConstraintsIfNeeded
:立即触发约束更新,自动更新布局。
使用技巧
避免压缩和拉伸
一般我们会通过以下代码来添加两个并列的视图:
UILabel *typeLable = [[UILabel alloc] init];
typeLable.text = @"类型";
typeLable.textColor = [UIColor whiteColor];
typeLable.backgroundColor = [UIColor grayColor];
[self.view addSubview:typeLable];
[typeLable mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.view);
make.left.equalTo(self.view).offset(15);
}];
UILabel *titleLable = [[UILabel alloc] init];
titleLable.text = @"标题";
titleLable.textColor = [UIColor blackColor];
titleLable.backgroundColor = [UIColor orangeColor];
titleLable.font = [UIFont systemFontOfSize:25];
[self.view addSubview:titleLable];
[titleLable mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(typeLable.mas_right);
make.centerY.equalTo(typeLable);
make.right.equalTo(self.view).offset(-15);
}];
运行之后会发现左侧的View被拉伸了,但实际上我们会希望左边保持原有大小,而拉伸右侧的。这时我们可以再添加另外的约束来实现这个效果:
[typeLable setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
[typeLable setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
[titleLable setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];
[titleLable setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];
最后的结果:
使某个约束失效
这种情况一般出现九宫格图片时,可能需要设置contentView的bottom:
make.bottom.equalTo(self.contentView);
如果cell被复用了,就需要使之前的底部约束失效,重新再设置。所有可以用一个全局变量保存这个MASConstraint,在重新设置之前先调用[constraint uninstall]
。
解决常见的约束冲突警告
UITableViewCell 的约束冲突
2017-12-29 16:24:42.645364+0800 project[3804:770025] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
[
<MASLayoutConstraint:0x6040002b7d60 UILabel:0x7fa5e0c5c490.top == UITableViewCellContentView:0x7fa5e0c5c280.top + 10>,
<MASLayoutConstraint:0x6040002b85a0 UILabel:0x7fa5e0c5c770.top == UILabel:0x7fa5e0c5c490.bottom + 10>,
<MASLayoutConstraint:0x6040002b8f00 UILabel:0x7fa5e0c5d010.top == UILabel:0x7fa5e0c5c770.bottom + 5>,
<MASLayoutConstraint:0x6040002b94a0 UILabel:0x7fa5e0c5d5d0.top == UILabel:0x7fa5e0c5d010.bottom + 5>,
<MASLayoutConstraint:0x6040002b9800 UILabel:0x7fa5e0c5d8b0.top == UILabel:0x7fa5e0c5d5d0.bottom + 5>,
<MASLayoutConstraint:0x6040002b9aa0 UILabel:0x7fa5e0c5d8b0.bottom == UITableViewCellContentView:0x7fa5e0c5c280.bottom - 10>,
<NSLayoutConstraint:0x600000290810 UITableViewCellContentView:0x7fa5e0c5c280.height == 43.6667>
]
Will attempt to recover by breaking constraint
<MASLayoutConstraint:0x6040002b85a0 UILabel:0x7fa5e0c5c770.top == UILabel:0x7fa5e0c5c490.bottom + 10>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
找到被覆盖的约束:<MASLayoutConstraint:0x6040002b85a0 UILabel:0x7fa5e0c5c770.top == UILabel:0x7fa5e0c5c490.bottom + 10>
,给它添加优先级即可消除警告:
make.top.equalTo(self.titleLable.mas_bottom).offset(10).priority(500);
自定义View的NSAutoresizingMaskLayoutConstraint冲突
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<MASLayoutConstraint:0x7fafcb1c8e10 UIButton:0x7fafcb18e160.width == UIView:0x7fafcb0bd380.width - 60>",
"<NSAutoresizingMaskLayoutConstraint:0x7fafcb0a2570 UIView:0x7fafcb0bd380.width == 0>"
)
Will attempt to recover by breaking constraint
<MASLayoutConstraint:0x7fafcb1c8e10 UIButton:0x7fafcb18e160.width == UIView:0x7fafcb0bd380.width - 60>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
解决方法:View的translatesAutoresizingMaskIntoConstraints设置为NO
将UIStackView的translatesAutoresizingMaskIntoConstraints设为NO
如果设置了UIStackView的Spacing可能会出现冲突,此时需要将UIStackView的translatesAutoresizingMaskIntoConstraints设为NO。
源码解析
Masonry之所以可以如此简洁地添加约束,一个主要原因是它可以使用链式语法。在后面的源码阅读中可以重点看看它是如何实现的。
添加一个简单的约束:
UIView *view = [[UIView alloc] init];
[self.view addSubview:view];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view);
}];
跟踪代码会发现它进入了分类的一个方法中:
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
block(constraintMaker);
return [constraintMaker install];
}
这里做了4件事:
- 1、将View的
translatesAutoresizingMaskIntoConstraints
属性设置为NO; - 2、初始化了一个
MASConstraintMaker
; - 3、调用添加约束的block;
- 4、调用
[constraintMaker install]
;
这里View并没有持有block,block执行后就会释放,所有不会形成循环引用。
来到我们写的添加约束的代码中make.left.equalTo(self.view);
:
首先是make.left
,来到MASConstraintMaker
的edges方法,返回的是一个MASConstraint
:
- (MASConstraint *)left {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}
最终会来到:
- (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;
}
- 1、根据NSLayoutAtrribute生成MASViewAttribute;
- 2、根据MASViewAtrribute生成MASViewConstraint,这个viewAtrribute是newConstraint的firstViewAttribute;
- 3、如果传来的constraint不为空且为MASViewConstraints,则生成一个MASCompositeConstraint,并替换原有的constraint;
- 4、否则将newConstraint的delegate设为此MASConstaintMaker, 并保存;
这里的delegate都是为了实现链式调用而实现的,因为我们可能会这样调用make.left.right
,make.left返回的是MASViewConstraint,它也有right方法:
- (MASConstraint *)right {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight];
}
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");
return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
}
最后调用的是delegate的方法,从前面可以知道delegate其实就是MASConstraintMaker,所以最后回到MASConstraintMaker来创建。
这里调用的[self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute]
传入了self,也就是MASViewConstraint(make.left),最后会被新的MASCompositeConstraint替换。
到目前为止都是为了创建MASConstraint,主要出现了三个类,MASConstraintMaker
、MASViewConstraint
、MASCompositeConstraint
。
MASConstraintMaker就是为了给View创建MASConstraint的。它有个属性constraints用来保存每一个约束。
MASViewConstraint和MASCompositeConstraint都继承自MASConstraint。
MASViewConstraint有两个重要的属性,firstViewAttribute和secondViewAttribute,都是MASViewAttribute,而MASViewAttribute保存的又是NSLayoutAttribute。来看原生的创建约束的方法:
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top]
可以看到有两个attribute,MASViewConstraint的这两个MASViewAtrribute也就是要用在这里的。
MASCompositeConstraint可以看做一组MASViewConstraint的集合。它有个childConstraints用来保存它的子约束。在调用make.edges时返回的就是MASCompositeConstraint:
- (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 | MASAttributeBaseline
| MASAttributeFirstBaseline | MASAttributeLastBaseline
#if TARGET_OS_IPHONE || TARGET_OS_TV
| 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 (attrs & MASAttributeFirstBaseline) [attributes addObject:self.view.mas_firstBaseline];
if (attrs & MASAttributeLastBaseline) [attributes addObject:self.view.mas_lastBaseline];
#if TARGET_OS_IPHONE || TARGET_OS_TV
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;
}
这里主要做了以下5件事:
- 1、将
MASAtrribute
转为MASViewAtrribute数组
,比如把make.edges转为@[self.view.mas_left, self.view.mas_right, self.view.mas_top, self.view.mas_bottom]; - 2、再把
MASViewAtrribute数组
转为MASViewConstraint数组
; - 3、创建最终的MASCompositeConstraint,
MASViewConstraint数组
就是它的childConstraints; - 4、保存上面创建的MASCompositeConstraint到MASConstraintMaker的constraints中,以备后面install。
到目前为止拿到的都是firstViewAtrribute,接下来的.equalTo等则是拿到secondViewAtrribute。
.equalTo(self.view)其实是链式语法,调用.equalTo得到一个block,然后调用这个block,相当于:
id(^block)(id attribute) = constraint.mas_equalTo;
if (block) {
block(self.view);
}
之所以要返回block,是因为它要传递参数。源码:
- (MASConstraint *(^)(id))equalTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
};
}
来到MASViewConstraint的equalToWithRelation(attribute, NSLayoutRelationEqual),它同样是链式语法,这里的atrribute就是make.left.equalTo(self.view)中的self.view.mas_left。
- (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 {
NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @"Redefinition of constraint relation");
self.layoutRelation = relation;
self.secondViewAttribute = attribute;
return self;
}
};
}
因为这里我们的调用是make.left.equalTo(self.view),所以atrribute是View,直接走到self.secondViewAttribute = attribute
:
- (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]) {
MASViewAttribute *attr = secondViewAttribute;
if (attr.layoutAttribute == NSLayoutAttributeNotAnAttribute) {
_secondViewAttribute = [[MASViewAttribute alloc] initWithView:attr.view item:attr.item layoutAttribute:self.firstViewAttribute.layoutAttribute];;
} else {
_secondViewAttribute = secondViewAttribute;
}
} else {
NSAssert(NO, @"attempting to add unsupported attribute: %@", secondViewAttribute);
}
}
因为是View,他会根据firstViewAtrribute来创建secondViewAtrribute
_secondViewAttribute = [[MASViewAttribute alloc] initWithView:secondViewAttribute layoutAttribute:self.firstViewAttribute.layoutAttribute];
到这secondViewAtrribute也拿到了。
设置偏差值:make.left.equalTo(self.view).offset(5):
- (MASConstraint *(^)(NSValue *value))valueOffset {
return ^id(NSValue *offset) {
NSAssert([offset isKindOfClass:NSValue.class], @"expected an NSValue offset, got: %@", offset);
[self setLayoutConstantWithValue:offset];
return self;
};
}
- (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);
}
}
添加约束完成后,开始MASConstraintMaker的install:
- (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;
}
关键:[constraint install]
这段代码很长,主要完成的工作:
- 1、通过firstViewAtrribute和secondViewAtrribute分别拿到对应的View和NSLayoutAttribute;
- 2、如果是类似make.left.equalTo(@10)这样添加的约束,则secondViewAtrribute是空,默认会把firstViewAtrribute的view的superView和NSLayoutAttribute作为第二个约束;
- 3、生成MASLayoutConstraint,其实就是继承自NSLayoutConstraint,多了一个参数mas_key,用来调试。当约束冲突时,可以设置View的mas_key来确定是哪个View发生了冲突;
- 4、设置installedView,也就是用来addConstraint的View:如果secondViewAtrribute的View不为空,则installedView为firstView和secondView最相近的superView;否则如果firstViewAtrribute是sizeAtrribute,那installedView就是firstViewAtrribute的View本身;否则installView就是firstViewAtrribute的View的superView;
- 5、调用原生API addConstraint,添加约束,并在View的mas_installedConstraints保存约束。
至此,Constraint设置完成。
找到最近的公共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;
}
mas_key在View的分类中也添加了:
@interface MAS_VIEW (MASAdditions)
@property (nonatomic, strong) id mas_key;
@end
实现的方法:
- (id)mas_key {
return objc_getAssociatedObject(self, @selector(mas_key));
}
- (void)setMas_key:(id)key {
objc_setAssociatedObject(self, @selector(mas_key), key, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
Masonry提供了一个宏来批量设置View的mas_key:
#define MASAttachKeys(...) \
{ \
NSDictionary *keyPairs = NSDictionaryOfVariableBindings(__VA_ARGS__); \
for (id key in keyPairs.allKeys) { \
id obj = keyPairs[key]; \
NSAssert([obj respondsToSelector:@selector(setMas_key:)], \
@"Cannot attach mas_key to %@", obj); \
[obj setMas_key:key]; \
} \
}
参考
网友评论