源碼閱讀:Masonry(七)——MASConstraintMaker

NO IMAGE

該文章閱讀的 Masonry 的版本為 1.1.0。

這個類可以說是 Masonry 的核心類了,根據該類的名字可以知道這個類是用於創建約束的工廠類。

1.公共枚舉

typedef NS_OPTIONS(NSInteger, MASAttribute) {
MASAttributeLeft = 1 << NSLayoutAttributeLeft,
MASAttributeRight = 1 << NSLayoutAttributeRight,
MASAttributeTop = 1 << NSLayoutAttributeTop,
MASAttributeBottom = 1 << NSLayoutAttributeBottom,
MASAttributeLeading = 1 << NSLayoutAttributeLeading,
MASAttributeTrailing = 1 << NSLayoutAttributeTrailing,
MASAttributeWidth = 1 << NSLayoutAttributeWidth,
MASAttributeHeight = 1 << NSLayoutAttributeHeight,
MASAttributeCenterX = 1 << NSLayoutAttributeCenterX,
MASAttributeCenterY = 1 << NSLayoutAttributeCenterY,
MASAttributeBaseline = 1 << NSLayoutAttributeBaseline,
MASAttributeFirstBaseline = 1 << NSLayoutAttributeFirstBaseline,
MASAttributeLastBaseline = 1 << NSLayoutAttributeLastBaseline,
MASAttributeLeftMargin = 1 << NSLayoutAttributeLeftMargin,
MASAttributeRightMargin = 1 << NSLayoutAttributeRightMargin,
MASAttributeTopMargin = 1 << NSLayoutAttributeTopMargin,
MASAttributeBottomMargin = 1 << NSLayoutAttributeBottomMargin,
MASAttributeLeadingMargin = 1 << NSLayoutAttributeLeadingMargin,
MASAttributeTrailingMargin = 1 << NSLayoutAttributeTrailingMargin,
MASAttributeCenterXWithinMargins = 1 << NSLayoutAttributeCenterXWithinMargins,
MASAttributeCenterYWithinMargins = 1 << NSLayoutAttributeCenterYWithinMargins,
};

這個枚舉對原有枚舉進行了二次封裝。

2.公共屬性

@property (nonatomic, strong, readonly) MASConstraint *left;
@property (nonatomic, strong, readonly) MASConstraint *top;
@property (nonatomic, strong, readonly) MASConstraint *right;
@property (nonatomic, strong, readonly) MASConstraint *bottom;
@property (nonatomic, strong, readonly) MASConstraint *leading;
@property (nonatomic, strong, readonly) MASConstraint *trailing;
@property (nonatomic, strong, readonly) MASConstraint *width;
@property (nonatomic, strong, readonly) MASConstraint *height;
@property (nonatomic, strong, readonly) MASConstraint *centerX;
@property (nonatomic, strong, readonly) MASConstraint *centerY;
@property (nonatomic, strong, readonly) MASConstraint *baseline;
@property (nonatomic, strong, readonly) MASConstraint *firstBaseline;
@property (nonatomic, strong, readonly) MASConstraint *lastBaseline;
@property (nonatomic, strong, readonly) MASConstraint *leftMargin;
@property (nonatomic, strong, readonly) MASConstraint *rightMargin;
@property (nonatomic, strong, readonly) MASConstraint *topMargin;
@property (nonatomic, strong, readonly) MASConstraint *bottomMargin;
@property (nonatomic, strong, readonly) MASConstraint *leadingMargin;
@property (nonatomic, strong, readonly) MASConstraint *trailingMargin;
@property (nonatomic, strong, readonly) MASConstraint *centerXWithinMargins;
@property (nonatomic, strong, readonly) MASConstraint *centerYWithinMargins;

上面這一些屬性分別對應著當前視圖的約束屬性,也就是 view1attr1


/**
這個屬性可以同時設置 NSLayoutAttributeTop、NSLayoutAttributeLeft、NSLayoutAttributeBottom 和 NSLayoutAttributeRight 這四個約束屬性
*/
@property (nonatomic, strong, readonly) MASConstraint *edges;
/**
這個屬性可以同時設置 NSLayoutAttributeWidth 和 NSLayoutAttributeHeight 這兩個約束屬性
*/
@property (nonatomic, strong, readonly) MASConstraint *size;
/**
這個屬性可以同時設置 NSLayoutAttributeCenterX 和 NSLayoutAttributeCenterY 這兩個約束屬性
*/
@property (nonatomic, strong, readonly) MASConstraint *center;

這三個屬性也是用來設置當前視圖的約束屬性的,只不過做了封裝,可以更方便簡潔的進行設置,具體


@property (nonatomic, strong, readonly) MASConstraint *(^attributes)(MASAttribute attrs);

這個屬性是一個 block 類型的,可以直接設置屬性。

具體的用法如下:

如果使用 MASConstraintMaker 提供屬性設置是這樣的:

make.top.left.bottom.right.equalTo(self.view);

如果直接使用屬性 attributes 進行設置就是這樣的:

make.attributes(MASAttributeTop | MASAttributeLeft | MASAttributeBottom | MASAttributeRight).equalTo(self.view);

上面兩種設置方式的效果是相同的。


/**
在添加約束時是否檢查該約束已經被添加,用於方法 mas_updateConstraints: 中
*/
@property (nonatomic, assign) BOOL updateExisting;
/**
在添加約束時是否移除已添加的約束,由於方法 mas_remakeConstraints 中
*/
@property (nonatomic, assign) BOOL removeExisting;

3.公共方法

/**
以指定視圖初始化本類對象的方法
*/
- (id)initWithView:(MAS_VIEW *)view;
/**
安裝通過該類對象添加的所有約束
*/
- (NSArray *)install;
/**
這個方法用於傳入一個 block 
具體這個方法是做什麼的不太清楚,沒有註釋,也沒有例子
從該方法的實現來看,好像是對該對象中的約束對象進行添加
*/
- (MASConstraint * (^)(dispatch_block_t))group;

4.類擴展

/**
保存 view1
*/
@property (nonatomic, weak) MAS_VIEW *view;
/**
保存設置的約束對象
*/
@property (nonatomic, strong) NSMutableArray *constraints;

5. 實現

5.1 私有方法

- (MASConstraint *)addConstraintWithAttributes:(MASAttribute)attrs {
// 定義變量獲取所有可設置的枚舉值
// __unused 的作用是:如果定義的變量未使用的話編譯器就會報警,添加 __unused 前綴以後就不會有報警
__unused MASAttribute anyAttribute = (MASAttributeLeft | MASAttributeRight | MASAttributeTop | MASAttributeBottom | MASAttributeLeading
| MASAttributeTrailing | MASAttributeWidth | MASAttributeHeight | MASAttributeCenterX
| MASAttributeCenterY | MASAttributeBaseline
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
| MASAttributeFirstBaseline | MASAttributeLastBaseline
#endif
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000)
| MASAttributeLeftMargin | MASAttributeRightMargin | MASAttributeTopMargin | MASAttributeBottomMargin
| MASAttributeLeadingMargin | MASAttributeTrailingMargin | MASAttributeCenterXWithinMargins
| MASAttributeCenterYWithinMargins
#endif
);
// 傳入的參數必須是枚舉中的枚舉值
NSAssert((attrs & anyAttribute) != 0, @"You didn't pass any attribute to make.attributes(...)");
// 創建變量保存視圖屬性封裝對象
NSMutableArray *attributes = [NSMutableArray array];
// 根據傳入的枚舉值添加相應的視圖屬性封裝對象
if (attrs & MASAttributeLeft) [attributes addObject:self.view.mas_left];
if (attrs & MASAttributeRight) [attributes addObject:self.view.mas_right];
if (attrs & MASAttributeTop) [attributes addObject:self.view.mas_top];
if (attrs & MASAttributeBottom) [attributes addObject:self.view.mas_bottom];
if (attrs & MASAttributeLeading) [attributes addObject:self.view.mas_leading];
if (attrs & MASAttributeTrailing) [attributes addObject:self.view.mas_trailing];
if (attrs & MASAttributeWidth) [attributes addObject:self.view.mas_width];
if (attrs & MASAttributeHeight) [attributes addObject:self.view.mas_height];
if (attrs & MASAttributeCenterX) [attributes addObject:self.view.mas_centerX];
if (attrs & MASAttributeCenterY) [attributes addObject:self.view.mas_centerY];
if (attrs & MASAttributeBaseline) [attributes addObject:self.view.mas_baseline];
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
if (attrs & MASAttributeFirstBaseline) [attributes addObject:self.view.mas_firstBaseline];
if (attrs & MASAttributeLastBaseline) [attributes addObject:self.view.mas_lastBaseline];
#endif
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000)
if (attrs & MASAttributeLeftMargin) [attributes addObject:self.view.mas_leftMargin];
if (attrs & MASAttributeRightMargin) [attributes addObject:self.view.mas_rightMargin];
if (attrs & MASAttributeTopMargin) [attributes addObject:self.view.mas_topMargin];
if (attrs & MASAttributeBottomMargin) [attributes addObject:self.view.mas_bottomMargin];
if (attrs & MASAttributeLeadingMargin) [attributes addObject:self.view.mas_leadingMargin];
if (attrs & MASAttributeTrailingMargin) [attributes addObject:self.view.mas_trailingMargin];
if (attrs & MASAttributeCenterXWithinMargins) [attributes addObject:self.view.mas_centerXWithinMargins];
if (attrs & MASAttributeCenterYWithinMargins) [attributes addObject:self.view.mas_centerYWithinMargins];
#endif
// 創建變量保存視圖約束封裝對象
NSMutableArray *children = [NSMutableArray arrayWithCapacity:attributes.count];
// 遍歷視圖屬性封裝對象
for (MASViewAttribute *a in attributes) {
// 創建視圖約束封裝對象,並添加到數組中
[children addObject:[[MASViewConstraint alloc] initWithFirstViewAttribute:a]];
}
// 創建多約束封裝對象
MASCompositeConstraint *constraint = [[MASCompositeConstraint alloc] initWithChildren:children];
// 設置代理對象
constraint.delegate = self;
// 添加到數組中保存
[self.constraints addObject:constraint];
// 多約束封裝對象
return constraint;
}
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
// 調用 MASConstraintDelegate 中的方法,並且第一個參數傳 nil,也就是創建 MASViewConstraint 類型對象
return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}

5.2 MASConstraintDelegate 方法

- (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint {
// 獲取要替換約束在當前數數中的索引
NSUInteger index = [self.constraints indexOfObject:constraint];
NSAssert(index != NSNotFound, @"Could not find constraint %@", constraint);
// 替換數組中地址索引的約束
[self.constraints replaceObjectAtIndex:index withObject:replacementConstraint];
}
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
// 根據當前視圖和傳入的參數創建視圖屬性封裝對象
MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
// 根據生成的視圖屬性封裝對象創建視圖約束封裝對象
MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
// 判斷參數是否是 MASViewConstraint 類型的
if ([constraint isKindOfClass:MASViewConstraint.class]) {
// 如果是,也就是設置多約束:make.top.left.equalTo(self.view); 
// 創建數組保存約束對象
NSArray *children = @[constraint, newConstraint];
// 創建多約束封裝對象
MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
// 設置代理對象
compositeConstraint.delegate = self;
// 用多約束對象替換之前的約束對象
[self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
// 返回新創建的多約束對象
return compositeConstraint;
}
if (!constraint) {
// 如果沒有傳參,也就是第一次設置
// 設置代理對象
newConstraint.delegate = self;
// 將約束對象添加到數組中保存
[self.constraints addObject:newConstraint];
}
// 返回約束對象
return newConstraint;
}

5.4 公共方法

- (id)initWithView:(MAS_VIEW *)view {
self = [super init];
if (!self) return nil;
// 保存參數
self.view = view;
// 初始化屬性
self.constraints = NSMutableArray.new;
return self;
}
- (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;
}
- (MASConstraint *)left {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}
- (MASConstraint *)top {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}
- (MASConstraint *)right {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight];
}
- (MASConstraint *)bottom {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottom];
}
- (MASConstraint *)leading {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeading];
}
- (MASConstraint *)trailing {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailing];
}
- (MASConstraint *)width {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeWidth];
}
- (MASConstraint *)height {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeHeight];
}
- (MASConstraint *)centerX {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterX];
}
- (MASConstraint *)centerY {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterY];
}
- (MASConstraint *)baseline {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBaseline];
}
- (MASConstraint *)firstBaseline {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeFirstBaseline];
}
- (MASConstraint *)lastBaseline {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLastBaseline];
}
- (MASConstraint *)leftMargin {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeftMargin];
}
- (MASConstraint *)rightMargin {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRightMargin];
}
- (MASConstraint *)topMargin {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTopMargin];
}
- (MASConstraint *)bottomMargin {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottomMargin];
}
- (MASConstraint *)leadingMargin {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeadingMargin];
}
- (MASConstraint *)trailingMargin {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailingMargin];
}
- (MASConstraint *)centerXWithinMargins {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterXWithinMargins];
}
- (MASConstraint *)centerYWithinMargins {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterYWithinMargins];
}

上面的這些方法都是直接調用 addConstraintWithLayoutAttribute: 方法,並且傳遞對應的參數,也就是不同的約束屬性

- (MASConstraint *)edges {
return [self addConstraintWithAttributes:MASAttributeTop | MASAttributeLeft | MASAttributeRight | MASAttributeBottom];
}
- (MASConstraint *)size {
return [self addConstraintWithAttributes:MASAttributeWidth | MASAttributeHeight];
}
- (MASConstraint *)center {
return [self addConstraintWithAttributes:MASAttributeCenterX | MASAttributeCenterY];
}
- (MASConstraint *(^)(MASAttribute))attributes {
return ^(MASAttribute attrs){
return [self addConstraintWithAttributes:attrs];
};
}

這四個方法調用的是 addConstraintWithLayoutAttribute: 方法,傳遞多個約束屬性。

- (MASConstraint *(^)(dispatch_block_t group))group {
return ^id(dispatch_block_t group) {
// 記錄執行 block 之前約束封裝對象的數量
NSInteger previousCount = self.constraints.count;
// 執行 block
group();
// 獲取執行 block 之後新增的約束封裝對象
NSArray *children = [self.constraints subarrayWithRange:NSMakeRange(previousCount, self.constraints.count - previousCount)];
// 將新增的約束封裝對象封裝成一個多約束封裝對象
MASCompositeConstraint *constraint = [[MASCompositeConstraint alloc] initWithChildren:children];
// 設置代理
constraint.delegate = self;
// 返回多約束封裝對象
return constraint;
};
}

6. 總結

這個類是用來創建約束封裝對象,無論是視圖約束封裝對象,還是多視圖約束封裝對象。該類雖然是繼承自 NSObject,但是也實現了 MASConstraint 類相同的鏈式編程的效果。

相關文章

GCDAsyncSocket源碼閱讀

[UIViewhitTest:withEvent:]方法總結

源碼閱讀:Masonry(九)——NSArray+MASAdditions/其他分類

源碼閱讀:Masonry(八)——View+MASAdditions