Masonry源码浅析

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);
}];
  • 源码
  1. 首先来看mas_makeConstraints,内部初始化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];
}
  1. 然后先回到外面来,看一下make.leftright,top,height等等都一样),最终MASConstraintMaker的操作都会加到约束数组中:
@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];//添加到数组
    }
    return newConstraint;
}
  1. 添加到约束数组之后返回了MASConstraint,接着来看看equalTooffset
- (MASConstraint * (^)(id))equalTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}

- (MASConstraint * (^)(CGFloat))offset {
    return ^id(CGFloat offset){
        self.offset = offset;
        return self;
    };
}
  • equalTooffset其实都是block形式,所以调用时可以用()而不用[],而且都返回MASConstraint,所以可以连续调用,紧接着make.left使用链式语法。
  1. block(constraintMaker)执行完后,就进行[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移除旧约束,再遍历约束数组进行[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给控件添加约束或者更新约束。

  1. 另外,上面出现的self.updateExisting会在mas_updateConstraints里设置,self.removeExisting会在mas_remakeConstraints里设置:
- (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会返回该View的约束数组,通过block传送MASConstraintMaker(负责管理约束)添加约束,block完毕后,
MASConstraintMaker调用install,然后会遍历约束数组,每一个MASViewConstraint都调用install,最终会调用原生的方法添加约束。

推荐阅读更多精彩内容