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

NO IMAGE

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

這個分類提供了為視圖數組中的每一個視圖設置約束的便捷方法。

1.公共枚舉

typedef NS_ENUM(NSUInteger, MASAxisType) {
MASAxisTypeHorizontal,
MASAxisTypeVertical
};

這個枚舉定義了水平和垂直兩個方向。

2.公共方法

/**
為視圖數組中每一個視圖添加並安裝約束的方法
*/
- (NSArray *)mas_makeConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block;
/**
為視圖數組中每一個視圖更新已安裝約束的方法
*/
- (NSArray *)mas_updateConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block;
/**
為視圖數組中每一個視圖卸載已安裝的約束並重新添加並安裝約束的方法
*/
- (NSArray *)mas_remakeConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block;
/**
使視圖數組中的視圖元素在水平或者垂直的方向以固定間距的方式進行排列的方法
*/
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
/**
使視圖數組中的視圖元素在水平或者垂直的方向以固定大小的方式進行排列的方法
*/
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;

3.實現

3.1 私有方法

/**
該方法用於獲取視圖數組中所有視圖元素的公共父視圖
*/
- (MAS_VIEW *)mas_commonSuperviewOfViews
{
// 創建變量保存公共父視圖
MAS_VIEW *commonSuperview = nil;
// 創建變量保存前一個視圖
MAS_VIEW *previousView = nil;
// 遍歷當前數組中每一個視圖元素
for (id object in self) {
// 數組中的元素必須是 UIView 及其子類
if ([object isKindOfClass:[MAS_VIEW class]]) {
// 創建變量保存當前視圖
MAS_VIEW *view = (MAS_VIEW *)object;
if (previousView) {
// 如果這不是第一個元素,那獲取當前視圖和前一個視圖的公共父視圖
commonSuperview = [view mas_closestCommonSuperview:commonSuperview];
} else {
// 如果這是第一個元素,那公共父視圖就是當前視圖
commonSuperview = view;
}
// 保存當前視圖,用於下次循環
previousView = view;
}
}
NSAssert(commonSuperview, @"Can't constrain views that do not share a common superview. Make sure that all the views in this array have been added into the same view hierarchy.");
// 返回公共父視圖
return commonSuperview;
}

3.2 公共方法

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block {
NSMutableArray *constraints = [NSMutableArray array];
for (MAS_VIEW *view in self) {
NSAssert([view isKindOfClass:[MAS_VIEW class]], @"All objects in the array must be views");
[constraints addObjectsFromArray:[view mas_makeConstraints:block]];
}
return constraints;
}
- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block {
NSMutableArray *constraints = [NSMutableArray array];
for (MAS_VIEW *view in self) {
NSAssert([view isKindOfClass:[MAS_VIEW class]], @"All objects in the array must be views");
[constraints addObjectsFromArray:[view mas_updateConstraints:block]];
}
return constraints;
}
- (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block {
NSMutableArray *constraints = [NSMutableArray array];
for (MAS_VIEW *view in self) {
NSAssert([view isKindOfClass:[MAS_VIEW class]], @"All objects in the array must be views");
[constraints addObjectsFromArray:[view mas_remakeConstraints:block]];
}
return constraints;
}

這三個方法都是遍歷數組,獲取數組中的視圖元素,然後調用視圖元素各自的分類方法設置約束,最後返回約束對象數組。


- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing {
// 必須有 1 個以上的視圖元素
if (self.count < 2) {
NSAssert(self.count>1,@"views to distribute need to bigger than one");
return;
}
// 獲取這些視圖的公共父視圖
MAS_VIEW *tempSuperView = [self mas_commonSuperviewOfViews];
if (axisType == MASAxisTypeHorizontal) {
// 如果設置的是水平方向佈局
// 創建變量保存前一個視圖
MAS_VIEW *prev;
// 遍歷視圖數組
for (int i = 0; i < self.count; i++) {
// 獲取視圖元素
MAS_VIEW *v = self[i];
// 添加並安裝約束的方法
[v mas_makeConstraints:^(MASConstraintMaker *make) {
if (prev) {
// 如果當前視圖不是第一個視圖
// 當前視圖寬度等於前一個視圖寬度
make.width.equalTo(prev);
// 當前視圖左邊距離前一個視圖右邊指定的間距
make.left.equalTo(prev.mas_right).offset(fixedSpacing);
if (i == self.count - 1) {
// 如果是最後一個視圖
// 當前視圖的右邊距離公共父視圖右邊指定的尾距
make.right.equalTo(tempSuperView).offset(-tailSpacing);
}
}
else {
// 如果當前視圖是第一個視圖
// 當前視圖的左邊距離公共父視圖左邊指定的首距
make.left.equalTo(tempSuperView).offset(leadSpacing);
}
}];
// 保存當前視圖,用於下次循環
prev = v;
}
}
else {
// 如果設置的是垂直方向佈局
// 創建變量保存前一個視圖
MAS_VIEW *prev;
// 遍歷視圖數組
for (int i = 0; i < self.count; i++) {
// 獲取視圖元素
MAS_VIEW *v = self[i];
// 添加並安裝約束的方法
[v mas_makeConstraints:^(MASConstraintMaker *make) {
if (prev) {
// 如果當前視圖不是第一個視圖
// 當前視圖高度等於前一個視圖高度
make.height.equalTo(prev);
// 當前視圖上邊距離前一個視圖上邊指定的間距
make.top.equalTo(prev.mas_bottom).offset(fixedSpacing);
if (i == self.count - 1) {
// 如果是最後一個視圖
// 當前視圖的下邊距離公共父視圖下邊指定的尾距
make.bottom.equalTo(tempSuperView).offset(-tailSpacing);
}                    
}
else {
// 如果當前視圖是第一個視圖
// 當前視圖的上邊距離公共父視圖上邊指定的首距
make.top.equalTo(tempSuperView).offset(leadSpacing);
}
}];
// 保存當前視圖,用於下次循環
prev = v;
}
}
}
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing {
// 必須有 1 個以上的視圖元素
if (self.count < 2) {
NSAssert(self.count>1,@"views to distribute need to bigger than one");
return;
}
// 獲取這些視圖的公共父視圖
MAS_VIEW *tempSuperView = [self mas_commonSuperviewOfViews];
if (axisType == MASAxisTypeHorizontal) {
// 如果設置的是水平方向佈局
// 創建變量保存前一個視圖
MAS_VIEW *prev;
// 遍歷視圖數組
for (int i = 0; i < self.count; i++) {
// 獲取當前視圖元素
MAS_VIEW *v = self[i];
// 添加並安裝約束的方法
[v mas_makeConstraints:^(MASConstraintMaker *make) {
// 當前視圖的寬度等於指定的寬度
make.width.equalTo(@(fixedItemLength));
if (prev) {
// 如果當前視圖不是第一個視圖
if (i == self.count - 1) {
// 如果是最後一個視圖
// 當前視圖的右邊距離公共父視圖右邊指定的尾距
make.right.equalTo(tempSuperView).offset(-tailSpacing);
}
else {
// 如果不是最後一個視圖
// 計算偏移量
CGFloat offset = (1-(i/((CGFloat)self.count-1)))*(fixedItemLength+leadSpacing)-i*tailSpacing/(((CGFloat)self.count-1));
// 當前視圖的右邊距離公共父視圖的右邊計算出的大小
make.right.equalTo(tempSuperView).multipliedBy(i/((CGFloat)self.count-1)).with.offset(offset);
}
}
else {
// 如果當前視圖是第一個視圖
// 當前視圖的左邊距離公共父視圖左邊指定的首距
make.left.equalTo(tempSuperView).offset(leadSpacing);
}
}];
// 保存當前視圖,用於下次循環
prev = v;
}
}
else {
// 如果設置的是垂直方向佈局
// 創建變量保存前一個視圖
MAS_VIEW *prev;
// 遍歷視圖數組
for (int i = 0; i < self.count; i++) {
// 獲取當前視圖元素
MAS_VIEW *v = self[i];
// 添加並安裝約束的方法
[v mas_makeConstraints:^(MASConstraintMaker *make) {
// 當前視圖的高度等於指定的高度
make.height.equalTo(@(fixedItemLength));
if (prev) {
// 如果當前視圖不是第一個視圖
if (i == self.count - 1) {
// 如果是最後一個視圖
// 當前視圖的底邊距離公共父視圖底邊指定的尾距
make.bottom.equalTo(tempSuperView).offset(-tailSpacing);
}
else {
// 如果不是最後一個視圖
// 計算偏移量
CGFloat offset = (1-(i/((CGFloat)self.count-1)))*(fixedItemLength+leadSpacing)-i*tailSpacing/(((CGFloat)self.count-1));
// 當前視圖的底邊距離公共父視圖的底邊計算出的大小
make.bottom.equalTo(tempSuperView).multipliedBy(i/((CGFloat)self.count-1)).with.offset(offset);
}
}
else {
// 如果當前視圖是第一個視圖
// 當前視圖的頂邊距離公共父視圖頂邊指定的首距
make.top.equalTo(tempSuperView).offset(leadSpacing);
}
}];
// 保存當前視圖,用於下次循環
prev = v;
}
}
}

4.其他分類

剩下的幾個類都比較簡單,所以就在一篇裡寫了。

4.1 ViewController+MASAdditions

這個分類中提供的方法是為了方便創建 UIViewControllertopLayoutGuidebottomLayoutGuide 相關的視圖屬性封裝對象。

4.2 NSLayoutConstraint+MASDebugAdditions

該分類中沒有提供什麼方法,只是重寫了 NSLayoutConstraint 類的 description 方法,目的是為了我們在調試約束衝突的時候,使控制檯打印出的內容可讀性更強。

4.3 View+MASShorthandAdditions

想要使用這個分類提供的方法,得在 #import "Masonry.h" 之前先定義一個宏 MAS_SHORTHAND,這樣就可以省略 mas_ 前綴。

4.4 NSArray+MASShorthandAdditions

這個分類的作用和上一個分類的作用是相同的,都是為了省略前綴 mas_

相關文章

iOS持續集成(一)——fastlane使用

ObjectiveC之屬性

GCDAsyncSocket源碼閱讀

[UIViewhitTest:withEvent:]方法總結