Masonry源碼閱讀配合下面兩篇文章足矣。第一篇比較簡單影钉,主講大框架。第二篇比較詳細掘剪,細節(jié)點較多平委。那我呢?我來講講進階吧夺谁。講一些
Draveness blog
from cocoachina
先看看原生的布局是怎么做的廉赔。
UIView *superview = self.view;
UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];
UIView *view2 = [[UIView alloc] init];
view2.translatesAutoresizingMaskIntoConstraints = NO;
view2.backgroundColor = [UIColor blueColor];
[superview addSubview:view2];
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[superview addConstraints:@[
//view1 constraints
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:padding.left],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-padding.bottom],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:view2
attribute:NSLayoutAttributeLeft
multiplier:1
constant:-padding.right],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:view2
attribute:NSLayoutAttributeWidth
multiplier:1
constant:0],
//view2 constraints
[NSLayoutConstraint constraintWithItem:view2
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top],
[NSLayoutConstraint constraintWithItem:view2
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:view1
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:padding.left],
[NSLayoutConstraint constraintWithItem:view2
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-padding.bottom],
[NSLayoutConstraint constraintWithItem:view2
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1
constant:-padding.right],
]];
Masonry做的事情就用點語法方便的把整個過程封裝了起來。比如
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top]
等價于 Masonry 的匾鸥。當然此時是view1調(diào)用了makeConstraints函數(shù)
make.top.mas_equalTo(superview.top).offset(padding.top)multipliedBy(1);
讀 masonry源碼還需要有點語法+block 的基礎(chǔ)蜡塌,讀者自行補充。導(dǎo)讀開始勿负!show time~
Tip1:Autoresizing
self.translatesAutoresizingMaskIntoConstraints = NO;
self.translatesAutoresizingMaskIntoConstraints = NO;
關(guān)閉Autoresizing馏艾。 不懂的可以看看這個。如果是 YES奴愉,autolayout將無效攒至。
Autoresizing相關(guān) blog
Tip2:make.left.right.top.bottom發(fā)生了什么
make.left.right.top.bottom.mas_equalTo(superview)
到底發(fā)生了什么?一步一步推導(dǎo)躁劣!
make 是 MASConstraintMaker
make.left 是MASConstraintMaker的實例對象調(diào)用了 left 方法迫吐,make.left返回了newConstraint。記住newConstraint的類型是MASViewAttribute账忘,很重要志膀!
- (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]) {
...此處不是MASViewConstraint熙宇,所以忽略
}
if (!constraint) {
newConstraint.delegate = self;//設(shè)置了代理!
[self.constraints addObject:newConstraint];
}
return newConstraint;
}
make.left.right 溉浙,make.left返回的是MASViewAttribute烫止,所以這時候去MASViewAttribute的對象方法里面找 right。它的父類MASConstraint實現(xiàn)了 right 方法
- (MASConstraint *)right {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight];
}
然后
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
}
此時有一個代理戳稽,注意之前的代碼馆蠕! newConstraint.delegate = self
,代理是 make!所以有跑到了這里惊奇!
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:
MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
//這個時候constraint就是 make.left 產(chǎn)生的MASViewConstraint;ス!颂郎!
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;
}
//下面都不走了!上面已經(jīng)返回了乓序!
if (!constraint) {
//此處不走寺酪!
}
return newConstraint;
}
so make.left.right返回了MASCompositeConstraint。里面有兩個MASViewConstraint替劈。MASCompositeConstraint里有childConstraints寄雀,里面存放著一個又一個的MASViewConstraint。
make.left.right.top ---
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
[self constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
return self;
}
|
V
- (MASConstraint *)constraint:(MASConstraint __unused *)constraint addConstraintWithLayoutAttribute:
id<MASConstraintDelegate> strongDelegate = self.delegate;
MASConstraint *newConstraint = [strongDelegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
newConstraint.delegate = self;
[self.childConstraints addObject:newConstraint];
return newConstraint;
}
這里調(diào)用strongDelegate去做 add 約束的動作陨献。compositeConstraint.delegate = self;
strongDelegate就是 make咙俩,(make 內(nèi)心是崩潰的,怎么又是我J省)
- (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]) {
//此時是MASCompositeConstraint,所以也不發(fā)生膜蛔!
}
if (!constraint) {
//..不是 nil坛猪,不發(fā)生。
}
return newConstraint;
}
所以make.left.right.top其實就[self.childConstraints addObject:newConstraint];
添加了一個新的約束皂股。此時要注意一個細節(jié)墅茉,return newConstraint;
這個細節(jié)坑了我 N 久。這里返回了newConstraint呜呐,所以make.left.right.top返回的是MASViewConstraint就斤?不是。這里返回了MASViewConstraint蘑辑,但是沒有去接收這個約束洋机。MASCompositeConstraint返回的是 self。太狡詐了~~~
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
[self constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
return self;
}
so make.left.right.top返回MASCompositeConstraint洋魂,且添加了一個約束绷旗。
make.left.right.top.bottom 這里和上面一樣喜鼓。
這里再次總結(jié)下!
make.left->MASViewConstraint
make.left.right-> MASCompositeConstraint
make.left.right.top-> MASCompositeConstraint
make.left.right.top.bottom-> MASCompositeConstraint
addConstraint這個動作都會在 make 中發(fā)生衔肢。
最后 make.left.right.top.bottom.mas_equalTo(superview)
- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation {
return ^id(id attr, NSLayoutRelation relation) {
for (MASConstraint *constraint in self.childConstraints.copy) {
constraint.equalToWithRelation(attr, relation);
}
return self;
};
}
遍歷約束庄岖,相對于調(diào)用MASViewConstraint的equalTo方法。
Tip3:masory 巧妙架構(gòu)
這個是對tip2的補充角骤。
??MASConstraint
是MASViewConstraint
和MASCompositeConstraint
的父類隅忿。
??MASCompositeConstraint
的重點是有一個存放MASViewConstraint
的childConstraints
。由于繼承了MASConstraint
所以又可以調(diào)用MASConstraint
的所有方法邦尊。使鏈式語法可以繼續(xù)~
??這里有一個思想背桐!
??父類,子類胳赌,子類組牢撼。
??子類組用起來和子類沒區(qū)別,但實際發(fā)生鏈式語法之后疑苫,每次都把新生成的子類收集到了自己里面熏版,讓自己變大。
??make充當了一個啟動器捍掺,產(chǎn)生了第一個MASViewConstraint撼短,使后面鏈式可以跑起來! make 也充當了一個生成MASConstraint
生成器的角色挺勿,所有的MASConstraint
都來自make曲横。這簡直太妙了!我水平有限不瓶,不知道怎么恰當形容禾嫉。
Tip4:mas_closestCommonSuperview
尋找共同的父控件到底發(fā)生了什么?下面的代碼讓我一度很困惑蚊丐。我不能理解!closestCommonSuperview && firstViewSuperview
怎么可能會為0熙参,后來我意識到firstViewSuperview.superview的父控件是有限的。它最后可能會為 nil麦备。
- (instancetype)mas_closestCommonSuperview:(MAS_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;
}
他不能直接寫成這樣孽椰?
if self.superview == view.superview
return self.superview
else
return nil
然后我測試了下,兩個視圖的父控件不是一個凛篙,比如View1的爺控件等于 View2 的父控件黍匾,布局也是可以進行的。好吧呛梆,我 too naive锐涯。確實應(yīng)該寫成尋找共同最小父控件。
Tip5:NSLayoutAttributeLeftMargin是什么
iOS 8新增屬性填物。下面兩句話等價全庸!
make.leftMargin.equalTo(10);
make.left.equalTo(another.left).offset(10);
這么用在父控件上當然可以秀仲!但是!
make.leftMargin.equalTo(10);
make.left.equalTo(superview.left).offset(10);
注意
如果superview是控制器的 self.view壶笼。那布局會出問題神僵。會有一定的誤差。這是系統(tǒng)問題覆劈”@瘢可以看看官方文檔。
Tip6:優(yōu)先級
MASLayoutPriorityRequired = UILayoutPriorityRequired;
MASLayoutPriorityDefaultHigh = UILayoutPriorityDefaultHigh;
MASLayoutPriorityDefaultMedium = 500;
MASLayoutPriorityDefaultLow = UILayoutPriorityDefaultLow;
MASLayoutPriorityFittingSizeLevel = UILayoutPriorityFittingSizeLevel;
UILayoutPriorityRequired NS_AVAILABLE_IOS(6_0) = 1000; // A required constraint. Do not exceed this.
UILayoutPriorityDefaultHigh NS_AVAILABLE_IOS(6_0) = 750; // This is the priority level with which a button resists compressing its content.
UILayoutPriorityDefaultLow NS_AVAILABLE_IOS(6_0) = 250; // This is the priority level at which a button hugs its contents horizontally.
UILayoutPriorityFittingSizeLevel NS_AVAILABLE_IOS(6_0) = 50;
每一條約束默認都是必須的责语,必須的意思是1000炮障。我常用的就是這個。
//效果一樣坤候。
make.width.priority(749);
make.width.equalTo(@(10)).priorityLow();
//如果有兩條約束胁赢,控件的高為60.
//假如你在外部調(diào)用[globalconstraint deactivate],此時高度就變成了30.
//其實這么用起來和 update 差不多白筹。
make.height.equalTo(@30).priorityLow();
globalconstraint = make.height.equalTo(@60);
Tip7:group
我在網(wǎng)上找了很多 group 的用法智末,愣是沒找著。我簡單測試了下徒河。其實 group 的用處就是可以返回MASCompositeConstraint系馆。有什么用就靠你的想象力了!
make.width.height.equalTo(redView);
make.bottom.equalTo(blueView.top).offset(-padding);
make.group(^(){
make.top.greaterThanOrEqualTo(superview.top).offset(padding);
make.left.equalTo(superview.left).offset(padding);
make.right.equalTo(redView.left).offset(-padding);
});
make.height.equalTo(blueView.height);
make.width.height.equalTo(redView);
make.bottom.equalTo(blueView.top).offset(-padding);
make.top.greaterThanOrEqualTo(superview.top).offset(padding);
make.left.equalTo(superview.left).offset(padding);
make.right.equalTo(redView.left).offset(-padding);
make.height.equalTo(blueView.height);
最后附上本人 github 源碼備注顽照。歡迎交流技術(shù)由蘑!
Masonry源碼備注