Masonry 源碼解析

Masonry 源碼解析

Masonry.png

上圖是 Masonry 的大體結構。

Masonry 主要分兩層,一部分通過主要的用戶接口及通過 DSL 能力。另一部分則是核心功能代碼。

DSL

Masonry 通過自定義 DSL,讓用戶可以通過簡單的代碼實現(xiàn)布局。

mas_makeConstraints 是我們最常用的方法。

mas_makeConstraints

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    self.translatesAutoresizingMaskIntoConstraints = NO;
    
    // 構建一個 MASConstraintMaker 對象
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    
    // 將對象傳給 block
    block(constraintMaker);
    
    // 執(zhí)行 install 布局
    return [constraintMaker install];
}

mas_makeConstraints 在,給 block 傳入 MASConstraintMaker 對象。

left

MASConstraintMakerMASConstraint 中定義了大量的屬性。

15116177474777.jpg
- (MASConstraint *)left {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}

這些屬性的代碼,大概都長這樣。添加完實現(xiàn)后,又返回 MASConstraint 。這樣使得我們能夠通過 . 語法不斷的添加 Layout 屬性。

addConstraintWithLayoutAttribute:

MASConstraintMaker 中的 addConstraintWithLayoutAttribute: 最終調(diào)用 constraint:addConstraintWithLayoutAttribute:

- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
    
    // 生成新的約束
    MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
    
    // 對于約束有依賴的處理
    if ([constraint isKindOfClass:MASViewConstraint.class]) {
        //replace with composite constraint
        NSArray *children = @[constraint, newConstraint];
        MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
        compositeConstraint.delegate = self;
        [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
        return compositeConstraint;
    }
    
    // 對于 View 的依賴處理
    if (!constraint) {
        newConstraint.delegate = self;
        [self.constraints addObject:newConstraint];
    }
    return newConstraint;
}
  • MASCompositeConstraintMASViewConstraint 都是 MASConstraint 的子類。
  • 方法會返回 MASCompositeConstraintMASViewConstraint
  • constraint:addConstraintWithLayoutAttribute:delegate 的方法。MASConstraintMaker 的方法中將 newConstraint.delegate = self;, compositeConstraint.delegate = self;。使用在調(diào)用 MASConstraintleft 這樣的屬性時,最終還是會回到 MASConstraintMaker 的這個方法中。
  • constraint:addConstraintWithLayoutAttribute: 主要做了一件事,就是把 MASCompositeConstraintMASViewConstraint 對象存入數(shù)組中。等 mas_makeConstraints 進行 install 。

offset

MASConstraint 還存在一類屬性,是允許傳入?yún)?shù)的。 offset 就是最典型的例子。

- (MASConstraint * (^)(CGFloat))offset {
    return ^id(CGFloat offset){
        self.offset = offset;
        return self;
    };
}

所以,這類屬性時通過 block 實現(xiàn)。設置完屬性后返回 self 。這樣使得鏈式調(diào)用可以繼續(xù)。

equalTo & mas_equalTo

Masonry 定義了兩個預編譯宏。一個 MAS_SHORTHAND 用于控制可以使用 makeConstraints 方法調(diào)用。不需要 mas_ 作為前綴。

另一個是 MAS_SHORTHAND_GLOBALS

#define mas_equalTo(...)                 equalTo(MASBoxValue((__VA_ARGS__)))
#define mas_greaterThanOrEqualTo(...)    greaterThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_lessThanOrEqualTo(...)       lessThanOrEqualTo(MASBoxValue((__VA_ARGS__)))

#define mas_offset(...)                  valueOffset(MASBoxValue((__VA_ARGS__)))


#ifdef MAS_SHORTHAND_GLOBALS

#define equalTo(...)                     mas_equalTo(__VA_ARGS__)
#define greaterThanOrEqualTo(...)        mas_greaterThanOrEqualTo(__VA_ARGS__)
#define lessThanOrEqualTo(...)           mas_lessThanOrEqualTo(__VA_ARGS__)

#define offset(...)                      mas_offset(__VA_ARGS__)

#endif

equalTo 宏

如果 MAS_SHORTHAND_GLOBALS 定義了則 equalTo 等同與調(diào)用 equalTo(MASBoxValue((__VA_ARGS__)))

調(diào)用 equalTo 宏 -> 調(diào)用 mas_equalTo 宏 -> 調(diào)用 equalTo(MASBoxValue((__VA_ARGS__)))

但如果 MAS_SHORTHAND_GLOBALS 沒定義調(diào)用的是 MASConstraintequalTo 屬性。

MASBoxValue

#define MASBoxValue(value) _MASBoxValue(@encode(__typeof__((value))), (value))

MASBoxValue 最終調(diào)用了 _MASBoxValue 。做了一件事,就是把 int , double 等轉(zhuǎn)換為 NSNumber ; 把 CGPoint , CGSize 轉(zhuǎn)換為 NSValue 。

為什么要做一層轉(zhuǎn)換呢?看了 equalTo 屬性的定義我們就明白了。

equalTo 屬性

- (MASConstraint * (^)(id attr))equalTo;

equalTo 屬性的定義, 是個 block , 返回 MASConstraint , 接收一個 id 類型的入?yún)ⅰ?而 int , double , CGPoint 這些并非對象, 所以 需要通過 MASBoxValue 做個轉(zhuǎn)換。

總結

  • Masonry 的核心代碼主要在 MASConstraintMaker、 MASViewConstraintMASCompositeConstraint
  • Masonry 主要通過屬性和 block 的方式實現(xiàn)鏈式調(diào)用,自定義了一套 DSL 。
  • Masonry 定義了些宏,這些宏會改變 equalTo 這樣的一些操作的行為。在使用是需要特別注意 。
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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