想必在AFNetworking以後,Masonry成了廣大iOS開發者平常開發不可或缺的三方庫之一。它的使用真的很是簡單,例如:數組
[greenView makeConstraints:^(MASConstraintMaker *make) {
make.top.greaterThanOrEqualTo(superview.top).offset(padding);
make.left.equalTo(superview.left).offset(padding);
make.bottom.equalTo(blueView.top).offset(-padding);
make.right.equalTo(redView.left).offset(-padding);
make.width.equalTo(redView.width);
make.height.equalTo(redView.height);
make.height.equalTo(blueView.height);
}];
複製代碼
首先咱們提取一些「關鍵詞」,他們到底是啥意思?bash
1.makeConstraints:
佈局
2.make
ui
3.left、right、height...
spa
4.equalTo()
3d
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
block(constraintMaker);
return [constraintMaker install];
}
複製代碼
MASConstraintMaker
被傳入了block()中,即MASConstraintMaker
實例就是咱們的第二個「關鍵詞」---make
。從[constraintMaker install]
還可看出,make負責了約束的添加。後文詳細介紹make
。代理
translatesAutoresizingMaskIntoConstraints
,官方解釋大體爲:默認狀況下它是YES
,即view
的autoresizing mask
會自動成爲它的佈局。若是咱們但願手動佈局,須要將它設爲NO
code
從鏈式調用make.top.equalTo...
,咱們能夠看出,make
中定義了這些約束屬性,它們是這樣實現的:cdn
// step 1
- (MASConstraint *)left {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}
// step 2
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}
// step 3
- (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;
}
if (!constraint) {
newConstraint.delegate = self;
[self.constraints addObject:newConstraint];
}
return newConstraint;
}
複製代碼
MASViewAttribute
從實現文件中得知,MASViewAttribute = View + NSLayoutAttribute + item
,這是一個可變元組,存儲了View
和與它相關的約束信息。對象
MASViewConstraint
這就是一個約束,它包含firstViewAttribute
和secondViewAttribute
。
由step2可知,單純的約束屬性在該方法下的第一個參數都是nil
,因此咱們先直接看這種狀況下的step3的執行狀況。它被加入了一個約束數組中。
if (!constraint) {
newConstraint.delegate = self;
[self.constraints addObject:newConstraint];
}
複製代碼
4)鏈式調用的實現 咱們知道Masonry
鏈式調用是它廣受好評的優勢之一。那麼,是如何作到make.top.left
這樣的操做呢? 由step3得知,make.top
的返回類型是MASViewConstraint
,那MASViewConstraint
中是如何調用到left的呢? 從MASViewConstraint
的父類MASConstraint
能夠看到,這裏也定義了全部的佈局屬性,而這些佈局屬性的實現方式,以下,例:
// MASConstraint.m
- (MASConstraint *)top {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}
// MASViewConstraint.m
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");
return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
}
複製代碼
MASViewConstraint
把這個方法的具體實現委託給了代理方法。而在step3中,這個代理正是MASConstraintMaker
。不一樣的是,此時,step2方法的參數不在是nil
了,而是當前約束屬性。這也是的step3的處理邏輯不同了。(回到step3中再看看!)此時,MASCompositeConstraint
登場了,它是一個約束組合。以make.top.left...
爲例,這個約束組合包含了top
和left
,而且調用了shouldBeReplacedWithConstraint:
方法,以下:
- (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];
}
複製代碼
以make.top.left...
爲例,這個方法找到了以前存儲top
約束的位置,並替換成了約束組合。最終,top
和left
就一塊兒被加入和make
的約束數組中。
從上文看,咱們已經拿到了全部的約束。這些約束是如何加入到視圖上的?來看看install
方法。 摘錄一下MASConstraintMaker
中的install
:
- (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;
}
複製代碼
make
的處理相對簡單,再看看MASViewConstraint
自己是如何install
的。因爲方法太長,我不粘貼完整的代碼了(分開來會比較容易看懂)。大體的流程以下: 1.如何約束是設置width
和height
,這是視圖自身的屬性,則把當前視圖的父視圖做爲關聯視圖(代碼中爲secondLayoutItem
)。即這兩個約束是相對於父視圖設置的。
2.若是佈局上存在相對視圖,即一般寫法中的equalTo(someView.mas_top)
這樣,則找到這2個視圖最近的公共父視圖,並把約束添加在這個父視圖上。代碼以下:
if (self.secondViewAttribute.view) {
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;
}
複製代碼
3.最後,若是是更新約束操做,則找出須要更新的約束單獨修改;不然,爲視圖添加約束,並記錄在mas_installedConstraints
中。
到這裏,約束的添加已經徹底梳理明白了。
仍是得看具體實現方法:
- (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;
[self.delegate constraint:self shouldBeReplacedWithConstraint:compositeConstraint];
return compositeConstraint;
} else {
NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @"Redefinition of constraint relation");
self.layoutRelation = relation;
self.secondViewAttribute = attribute;
return self;
}
};
}
複製代碼
這裏就比較好理解了,若是傳入的是約束數組,則把他包裝成MASCompositeConstraint
,以一樣的方式加入到make
的constraints
中。若是是單個約束,則將其設爲secondViewAttribute
,用於install
的時候使用。
1.MASConstraintMaker
做爲工廠,生產一個個MASViewConstraint
約束對象。 2.MASViewConstraint
和MASCompositeConstraint
繼承於抽象類MASConstraint
,爲咱們提供了高度封裝的約束對象 3.View+MASAdditions
這個UIView
的擴展是Masonry
與外界交互的接口類,這樣很好的把複雜的約束邏輯封裝在內部管理,又提供了簡單的API供用戶使用。
你看懂了嘛~