Masonry 結構圖,本文會按照程序的執行來說解。 本文主要分兩部分,第一部分是約束的安裝,主要看約束的整個操做過程;第二部分分析Block代碼塊中的操做 (下圖是由做者青玉伏案 所作,做者作的很漂亮這裏拿來參考一下)。html
//添加約束
- (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;
複製代碼
其實這段代碼很簡單,就是建立一個約束製造者,將block中的約束添加後更新約束,更新約束和從新添加約束代碼相似,比第一種方式添加了updateExisting和removeExisting的設置數組
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
//translatesAutoresizingMaskIntoConstraints能夠自動將frame轉化爲約束,設置約束時須要關閉
self.translatesAutoresizingMaskIntoConstraints = NO;
//約束製造者 --- 初始化,而且初始化約束的數組
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
//設置block的約束 --- 回到block的操做
block(constraintMaker);
//升級
return [constraintMaker install];
}
複製代碼
- (instancetype)mas_closestCommonSuperview:(MAS_VIEW *)view {
//獲取兩個視圖最近的公共父視圖 -----> 就是說一層一層的父視圖找,直到兩個視圖的父視圖是一個的時候 跳出循環
//目的是爲了給兩個view添加約束時找到他的父視圖
MAS_VIEW *closestCommonSuperview = nil;
MAS_VIEW *secondViewSuperview = view;
while (!closestCommonSuperview && secondViewSuperview) {
MAS_VIEW *firstViewSuperview = self;
while (!closestCommonSuperview && firstViewSuperview) {
if (secondViewSuperview == firstViewSuperview) {
closestCommonSuperview = secondViewSuperview;
}
firstViewSuperview = firstViewSuperview.superview;
}
secondViewSuperview = secondViewSuperview.superview;
}
return closestCommonSuperview;
}
複製代碼
上面在分類中調用了MASConstraintMaker中的兩個方法,初始化操做和install操做,代碼以下less
- (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) {
//獲取view中存在的約束
NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
//獲取到的數組挨個調用卸載方法,刪除約束
for (MASConstraint *constraint in installedConstraints) {
[constraint uninstall];
}
}
//self.constraints將block中添加的約束進行遍歷
NSArray *constraints = self.constraints.copy;
for (MASConstraint *constraint in constraints) {
//設置約束的updateExisting,若是調用的是更新方法在此處進行更新
constraint.updateExisting = self.updateExisting;
//將約束安裝
[constraint install];
}
//安裝完成移除全部的約束
[self.constraints removeAllObjects];
return constraints;
}
複製代碼
爲了理清這個類的做用,咱們先看一下蘋果爲咱們提供的原生操做(以下),能夠猜想這個類主要的操做就是下面重點標記的那一步操做ui
UIView *superview = self.view;
UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[superview addConstraints:@[
//重點看這一步的操做
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top]
]];
複製代碼
這裏給view動態添加了一個mas_installedConstraints的get方法,裏面存儲了install中添加的方法,下面的方法會往這個數組中添加MASViewConstraint屬性spa
+ (NSArray *)installedConstraintsForView:(MAS_VIEW *)view {
return [view.mas_installedConstraints allObjects];
}
複製代碼
方法比較長,先看其中最重要的方法,感受和蘋果原生的使用很像,進入MASLayoutConstraint的頭文件發現 @interface MASLayoutConstraint : NSLayoutConstraint,其實就是在這將約束添加到視圖上的3d
//生成一個NSLayoutConstraint
MASLayoutConstraint *layoutConstraint
= [MASLayoutConstraint constraintWithItem:firstLayoutItem
attribute:firstLayoutAttribute
relatedBy:self.layoutRelation
toItem:secondLayoutItem
attribute:secondLayoutAttribute
multiplier:self.layoutMultiplier
constant:self.layoutConstant];
複製代碼
暫時忽略,代碼中講到mas_closestCommonSuperview方法能夠在這進行跳轉代理
- (void)install {
//判斷約束是否存在 不存在就直接返回
if (self.hasBeenInstalled) {
return;
}
//[self supportsActiveProperty]判斷是否能響應
if ([self supportsActiveProperty] && self.layoutConstraint) {
//讓約束起做用
self.layoutConstraint.active = YES;
//[self.firstViewAttribute.view.mas_installedConstraints addObject:self];
return;
}
//獲取父視圖和當前視圖及對應的約束 這裏的Attribute是視圖和約束的一個封裝類
MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item;
NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;
MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item;
NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute;
//若是secondViewAttribute不存在而且firstViewAttribute約束不是寬高,那麼就把view的父視圖及約束設置給secondViewAttribute
if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {
secondLayoutItem = self.firstViewAttribute.view.superview;
secondLayoutAttribute = firstLayoutAttribute;
}
//生成一個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;
//獲得兩個視圖當前的公共superview
if (self.secondViewAttribute.view) {
/******************************************* / mas_closestCommonSuperview 這個方法具體操做能夠回到View+MASAdditions類中查看就是上文告訴你們暫時跳過的地方 */
MAS_VIEW *closestCommonSuperview = [self.firstViewAttribute.view mas_closestCommonSuperview:self.secondViewAttribute.view];
NSAssert(closestCommonSuperview,
@"couldn't find a common superview for %@ and %@",
self.firstViewAttribute.view, self.secondViewAttribute.view);
self.installedView = closestCommonSuperview;
} else if (self.firstViewAttribute.isSizeAttribute) {
self.installedView = self.firstViewAttribute.view;
} else {
self.installedView = self.firstViewAttribute.view.superview;
}
//若是是更新的話,判斷具體更新的參數是什麼 可到layoutConstraintSimilarTo中查看
MASLayoutConstraint *existingConstraint = nil;
if (self.updateExisting) {
existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];
}
if (existingConstraint) {
// 若是隻是更新的話,更新一下這個約束
existingConstraint.constant = layoutConstraint.constant;
self.layoutConstraint = existingConstraint;
} else {
//添加layout到父視圖,而且把約束類添加到mas_installedConstraints中
[self.installedView addConstraint:layoutConstraint];
self.layoutConstraint = layoutConstraint;
[firstLayoutItem.mas_installedConstraints addObject:self];
}
}
複製代碼
看完了上面的代碼就能夠鬆一口氣了,下面的uninstall過程就很簡單了code
- (void)uninstall {
//判斷可否響應active
if ([self supportsActiveProperty]) {
//讓約束不起做用
self.layoutConstraint.active = NO;
//從數組裏刪除掉這個約束
[self.firstViewAttribute.view.mas_installedConstraints removeObject:self];
return;
}
//從父視圖刪除約束
[self.installedView removeConstraint:self.layoutConstraint];
self.layoutConstraint = nil;
self.installedView = nil;
//刪除約束
[self.firstViewAttribute.view.mas_installedConstraints removeObject:self];
}
複製代碼
這部分操做講述以前先上一張圖,不知道你們有沒有和我相同的疑問,那就是left和right調用顏色爲何不一樣,看完這部分你們就會有本身的答案了cdn
感興趣的朋友能夠把下面代碼粘貼到IDE試一下htm
[self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.and.bottom.equalTo(self);
make.top.equalTo(self.topView.mas_bottom);
make.height.equalTo(self.topView);
}];
複製代碼
咱們先來分析標題中的代碼調用的方式
left/right/top/bottom.....,調用的方法相同
- (MASConstraint *)top {
//添加不一樣的約束屬性 返回值是約束的屬性
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}
複製代碼
添加屬性到constraints數組中,第一次進入constraint爲空,那麼去掉第一個判斷的內容,代碼以下
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
//初始化一個MASViewAttribute,存放view和layoutAttribute
MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
//初始化一個MASViewConstraint
MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
if (!constraint) {
//設置代理方法
newConstraint.delegate = self;
//有的話就添加到constraints
[self.constraints addObject:newConstraint];
}
return newConstraint;
}
複製代碼
經過對equalTo的查找發現調用的是以下的代碼,能夠看到這部分方法中調用的代碼是相同的,因爲MASAttribute是一個抽象類,在類中沒有找到其實現方法,經過上一節返回類型MASViewAttribute查找其具體實現
- (MASConstraint * (^)(id))equalTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
};
}
- (MASConstraint * (^)(id))mas_equalTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
};
}
- (MASConstraint * (^)(id))greaterThanOrEqualTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationGreaterThanOrEqual);
};
}
- (MASConstraint * (^)(id))mas_greaterThanOrEqualTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationGreaterThanOrEqual);
};
}
- (MASConstraint * (^)(id))lessThanOrEqualTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationLessThanOrEqual);
};
}
- (MASConstraint * (^)(id))mas_lessThanOrEqualTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationLessThanOrEqual);
};
}
複製代碼
- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation {
return ^id(id attribute, NSLayoutRelation relation) {
if ([attribute isKindOfClass:NSArray.class]) {
NSAssert(!self.hasLayoutRelation, @"Redefinition of constraint relation");
NSMutableArray *children = NSMutableArray.new;
for (id attr in attribute) {
MASViewConstraint *viewConstraint = [self copy];
viewConstraint.layoutRelation = relation;
viewConstraint.secondViewAttribute = attr;
//約束數組
[children addObject:viewConstraint];
}
//這是一個組合約束
MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
compositeConstraint.delegate = self.delegate;
//把maker的constraints數組中的單個約束MASViewConstraint替換成MASCompositeConstraint這種組合約束
[self.delegate constraint:self shouldBeReplacedWithConstraint:compositeConstraint];
return compositeConstraint;
} else {
NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @"Redefinition of constraint relation");
//若是是NSValue直接給MASViewConstraint賦值
self.layoutRelation = relation;
self.secondViewAttribute = attribute;
return self;
}
};
}
複製代碼
咱們先來分析標題中的代碼調用的方式,區別於第一種,這種屬於組合形式
MASCompositeConstraint,一個組合約束類,集成自抽象類MASConstraint,其屬性childConstraints數組包含了其組合的MASViewConstraint約束類,也就是說當一個block代碼塊中一行添加的約束超過一個之後,masonry內部會將約束統一更新爲MASCompositeConstraint類,並在最後將MASCompositeConstraint實例添加到maker的constraints中
約束爲一個的時候與上一小節代碼執行順序沒有區別,也就是說執行make.left的時候與上文的調用方式相同,可是當調用到right的時候,程序的調用方式發生改變,改變以下(你們能夠對照源碼查看)
//調用.right首先調用下面代碼
#import "MASConstraint.h"
- (MASConstraint *)right {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight];
}
//接着會走子類方法
#import "MASViewConstraint.h"
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");
//能夠看到此時的constraint已經賦值了,delegate指向maker類
return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
}
//調用maker類中的代理方法
#import "MASConstraintMaker.h"
- (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;
//把maker的constraints數組中的單個約束MASViewConstraint替換成MASCompositeConstraint這種組合約束
[self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
return compositeConstraint;
}
//這部分就不會執行了
if (!constraint) {
newConstraint.delegate = self;
//有的話就添加到constraints
[self.constraints addObject:newConstraint];
}
return newConstraint;
}
複製代碼
至此masonry的分析就完成了,剩下的就是一次次的重複調用了
masonry的鏈式語法很漂亮,有時間會給你們分析一下