Masonry框架學習

Masonry

使用

  • 關鍵方法:- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block;
  • 約束方法除了equalTo,還有lessThanOrEqualTo和greaterThanOrEqualTo
  • 當已有約束需要更新時調用mas_updateConstraints方法
  • mas_remakeConstraints方法會先清除當前所有約束再布置
  • Masonry的五大參數:Attribute(屬性) ,Relation(關系),Multiplier(乘),Constant(大小),Priority(優先級)
    //一個系統的約束創建方法
    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeTop
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeTop
                                multiplier:1.0
                                  constant:padding.top]
                                  
    //mas_makeConstraints
    [view1 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(superView).with.offset(10);
        make.left.equalTo(superView).with.offset(10);
        make.bottom.equalTo(superView.mas_bottom).with.offset(-10);
        make.right.equalTo(superView.mas_right).with.offset(-10);
//        等價于
//        make.edges.equalTo(superView).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));
//        會自動調用view1.translatesAutoresizingMaskIntoConstraints = NO;
    }];
    //mas_remakeConstrains
    [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);
        }
    }];
    //mas_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_makeConstraints方法內部創建MASConstraintMaker對象,然后傳遞到block執行,完成后調用maker的install方法來確保約束被添加到視圖中
//設置約束方法
- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *))block {
    self.translatesAutoresizingMaskIntoConstraints = NO;
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    constraintMaker.updateExisting = YES;
    block(constraintMaker);
    return [constraintMaker install];
}
//MASConstraintMaker的install方法
- (NSArray *)install {
    //判斷是否有移除所有已存在的約束標志
    if (self.removeExisting) {
        NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
        for (MASConstraint *constraint in installedConstraints) {
            [constraint uninstall];
        }
    }
    //取出maker對象中的所有約束
    NSArray *constraints = self.constraints.copy;
    for (MASConstraint *constraint in constraints) {
    //設置已經設置了約束的標志
        constraint.updateExisting = self.updateExisting;
        //調用MASConstraint的install方法
        [constraint install];
    }
    [self.constraints removeAllObjects];
    return constraints;
}
//MASViewConstraint(MASConstraint子類)的install方法
- (void)install {
    ...
    ...
    MASLayoutConstraint *layoutConstraint
        = [MASLayoutConstraint constraintWithItem:firstLayoutItem
                                        attribute:firstLayoutAttribute
                                        relatedBy:self.layoutRelation
                                           toItem:secondLayoutItem
                                        attribute:secondLayoutAttribute
                                       multiplier:self.layoutMultiplier
                                         constant:self.layoutConstant];
    
    ...
    ...
    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];
    }
}

MASConstraint和其子類

  • MASConstraint提供了基礎屬性(left,top,leftMargin...),且是創建鏈式語法的必要要素
  • MASCompositeConstraint內有一個childConstraints的屬性,在布局的時候,會遍歷該數組,逐一進行install方法調用,make.edges,make.size返回的就是該類型
  • MASViewConstraint是承載支持AutoLayout真正的對象,他創建了一個NSLayoutConstraint的必要屬性,并將其添加到對應的view上;它的firstViewAttribute和secondViewAttribute屬性分別代表了對應要設置約束兩個view的屬性

Masonry鏈式語法實現原理

進入文件中可發現left,top..等maker屬性方法返回都是MASConstraint類型,而MASConstraint中也有這些相同的屬性方法,返回值依是MASCompositeConstraint對象,所有才能像make.top.left.right...這樣使用Masonry,具體實現原理如下

 1、MASConstraint的left方法:

    - (MASConstraint *)left {
        return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
    }
    它會跳轉到MASViewConstraint的addConstraintWithLayout...方法
--------------------------------------------    
    2、MASViewConstraint的addConstraintWithLayout...方法

    - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
        return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
    }
    它會調用delegate的constraint...delegate就是MASConstraintMaker,來看看吧
--------------------------------------------
    3、MASConstraintMaker的代理方法

    - (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
            MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
            [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
            return compositeConstraint;
    }
    創建的compositeConstraint布局時,就會調用children的install方法設置約束,從而達到通過點語法

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容