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,內(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];
}
  1. 然后先回到外面來,看一下make.leftright,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;
}
  1. 添加到約束數(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;
    };
}
  • equalTooffset其實都是block形式,所以調(diào)用時可以用()而不用[],而且都返回MASConstraint,所以可以連續(xù)調(diào)用,緊接著make.left使用鏈式語法。
  1. 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給控件添加約束或者更新約束。

  1. 另外,上面出現(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)用原生的方法添加約束。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容