OC 常用的約束框架是Masonry,而swift常用的是SnapKit,不過今天就只看看Masonry。
先看個例子:
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.offset(0);
make.top.offset(0);
make.right.offset(0);
make.height.offset(300);
}];
[lable mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.offset(600);
make.left.offset(100);
}];
[button mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(view.mas_bottom).offset(10);
make.left.equalTo(view);
make.height.offset(50);
make.width.offset(50);
}];
- 源碼
- 首先來看
mas_makeConstraints,內(nèi)部初始化MASConstraintMaker,然后用block送出去操作:
#define MAS_VIEW UIView
@implementation MAS_VIEW (MASAdditions)
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
block(constraintMaker);
return [constraintMaker install];
}
- 然后先回到外面來,看一下
make.left(right,top,height等等都一樣),最終MASConstraintMaker的操作都會加到約束數(shù)組中:
@implementation MASConstraintMaker
- (MASConstraint *)left {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
...
if (!constraint) {
newConstraint.delegate = self;
[self.constraints addObject:newConstraint];//添加到數(shù)組
}
return newConstraint;
}
- 添加到約束數(shù)組之后返回了
MASConstraint,接著來看看equalTo,offset:
- (MASConstraint * (^)(id))equalTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
};
}
- (MASConstraint * (^)(CGFloat))offset {
return ^id(CGFloat offset){
self.offset = offset;
return self;
};
}
-
equalTo,offset其實都是block形式,所以調(diào)用時可以用()而不用[],而且都返回MASConstraint,所以可以連續(xù)調(diào)用,緊接著make.left使用鏈式語法。
-
block(constraintMaker)執(zhí)行完后,就進行[constraintMaker install]:
@implementation MASConstraintMaker
- (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;
}
會先判斷self.removeExisting移除舊約束,再遍歷約束數(shù)組進行[constraint install]:
- (void)install {
...
//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;
//找出約束添加到哪個view上,并賦值給self.installedView
//self.firstViewAttribute.view是要約束的view,self.secondViewAttribute.view是作為參考的view
if (self.secondViewAttribute.view) {
MAS_VIEW *closestCommonSuperview = [self.firstViewAttribute.view mas_closestCommonSuperview:self.secondViewAttribute.view];
self.installedView = closestCommonSuperview;//第一個和第二個view共同最近的一個superView
} 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];
}
}
添加約束的控件默認是父控件,如果固定寬高則為自身,如果有self.secondViewAttribute.view則找出共同父控件,最終判斷self.updateExisting給控件添加約束或者更新約束。
- 另外,上面出現(xiàn)的
self.updateExisting會在mas_updateConstraints里設(shè)置,self.removeExisting會在mas_remakeConstraints里設(shè)置:
- (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];
}
- 總結(jié)
mas_makeConstraints會返回該View的約束數(shù)組,通過block傳送MASConstraintMaker(負責管理約束)添加約束,block完畢后,
MASConstraintMaker調(diào)用install,然后會遍歷約束數(shù)組,每一個MASViewConstraint都調(diào)用install,最終會調(diào)用原生的方法添加約束。
