自动布局第三方库masonry,让我们能够用链式表达式的方式,优雅地写出自动布局的代码。
masonry 给我们便利的同时,使用过程中又给了我们一些困扰。有的时候布局不正确,却不知道是什么原因。纠结好久发现用错了方法。
到底何时用make, update,remake.今天我就结合源代码,来说说我的理解。个人想法仅供参考。
我们经常用到的有以下三种方法,我们通过源代码及结合实际情况,进行对比,分析和总结。
mas_makeConstraints, mas_updateConstraints, mas_remakeConstraints。
mas_makeConstraints
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
block(constraintMaker);
return [constraintMaker install];
}
mas_updateConstraints
- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
constraintMaker.updateExisting = YES;
block(constraintMaker);
return [constraintMaker install];
}
mas_remakeConstraints
- (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
constraintMaker.removeExisting = YES;
block(constraintMaker);
return [constraintMaker install];
}
观察以上三个方法中,唯一不同的是
update 方法有个updateExisting
remake 方法有个removeExisting
其他都没有什么区别。
然后让我们读一读install方法究竟用这两个Flag值做了什么?
updateExisting
removeExisting
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;
}
removeExisting 有值的情况下废弃了所有的约束。
MASViewConstraint install
- (void)install {
if (self.hasBeenInstalled) {
return;
}
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;
}
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) {
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;
}
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];
}
}
updateExisting 被赋值为YES的情况下,则会将约束更新为最新的约束。
注意:对于update 来说,只需要更新你需要更新的那个约束,不需要更新的约束是可以不设置。
但是对于remake来说,需要设置全面的约束条件,否则约束不全面会使UI展现有错误或者有隐患。
以下是官方文档给我们提供的使用建议:
Sometimes you need modify existing constraints in order to animate or remove/replace constraints. In Masonry there are a few different approaches to updating constraints.
有时,您需要修改现有约束,以便动画或删除/替换约束。在Masonry中,有几种不同的更新约束的方法。
- References
You can hold on to a reference of a particular constraint by assigning the result of a constraint make expression to a local variable or a class property. You could also reference multiple constraints by storing them away in an array.
通过将约束表达式的结果赋值给局部变量和属性,可以保持对特定约束的引用。你也可以通过多个约束存储在一个数组中来引用他们。
// in public/private interface
@property (nonatomic, strong) MASConstraint *topConstraint;
...
// when making constraints
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
self.topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top);
make.left.equalTo(superview.mas_left).with.offset(padding.left);
}];
...
// then later you can call
[self.topConstraint uninstall];
- mas_updateConstraints
Alternatively if you are only updating the constant value of the constraint you can use the convience method mas_updateConstraints instead of mas_makeConstraints
或者,如果你只更新约束的常量值,可以直接使用便利的方法mas_updateConstraints,而不是mas_makeConstraints。
// this is Apple's recommended place for adding/updating constraints
// this method can get called multiple times in response to setNeedsUpdateConstraints
// which can be called by UIKit internally or in your code if you need to trigger an update to your constraints
- (void)updateConstraints {
[self.growingButton mas_updateConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self);
make.width.equalTo(@(self.buttonSize.width)).priorityLow();
make.height.equalTo(@(self.buttonSize.height)).priorityLow();
make.width.lessThanOrEqualTo(self);
make.height.lessThanOrEqualTo(self);
}];
//according to apple super should be called at end of method
[super updateConstraints];
}
- mas_remakeConstraints
mas_updateConstraints is useful for updating a set of constraints, but doing anything beyond updating constant values can get exhausting. That’s where mas_remakeConstraints comes in.
mas_remakeConstraints is similar to mas_updateConstraints, but instead of updating constant values, it will remove all of its constraints before installing them again. This lets you provide different constraints without having to keep around references to ones which you want to remove.
mas_updateConstraints 在更新一系列约束有用,但是除了更新常量值,做其他的事情有些力不从心。这时,mas_remakeConstraints就登场了。
mas_remakeConstraints 类似于mas_updateConstraints,但是不同更新常量秩,它再次install之前,会移除所有的约束。这允许你提供不同的约束,不必须保留对要删除约束的引用。
- (void)changeButtonPosition {
[self.button mas_remakeConstraints:^(MASConstraintMaker *make) {
make.size.equalTo(self.buttonSize);
if (topLeft) {
make.top.and.left.offset(10);
} else {
make.bottom.and.right.offset(-10);
}
}];
}