追求Masonry

追求Masonry

?

小笨狼?關(guān)注

?1.4?2015.12.06 15:12*?字?jǐn)?shù) 4204?閱讀 20062評論 29喜歡 220贊賞 3

Autolayout就像一個知情達理,善解人意的好姑娘,可惜長相有點不堪入目,所以追求者寥寥無幾。所幸遇到了化妝大師cloudkite,給她來了一個完美的化妝,從此丑小鴨Autolayout變成了美天鵝Masonry。前幾日有幸一見,果然名不虛傳,長相甜美,還善解人意。我果斷放棄了Frame,開始追求Masonry

初識Masonry

初見

我們先來看看Masonry到底有多美。

我要設(shè)置一個containView,他距離superView的上下左右邊距都是10。

如果我用frame,應(yīng)該是這樣寫的:

UIEdgeInsetsedge =UIEdgeInsetsMake(10,10,10,10);CGSizesuperSize = view.superview.frame.size;CGFloatwidth = superSize.width - edge.left - edge.right;CGFloatheitht = superSize.height - edge.top - edge.bottom;view.frame =CGRectMake(edge.left, edge.top, width, heitht);

邏輯比較復(fù)雜,閱讀的時候還得想半天才能想明白,這個frame到底要表達的是什么意思。而且關(guān)鍵的是父View的大小如果改變,還需要再次重新設(shè)置Frame??粗鳩rame這黃臉婆,心里一陣別扭...

我們來看看充滿青春活力的小鮮肉Masonry是怎么樣的:

UIEdgeInsetsedge =UIEdgeInsetsMake(10,10,10,10);[view mas_makeConstraints:^(MASConstraintMaker *make) {? ? make.edges.equalTo(view.superview).insets(edge);}];

使用mas_makeConstraints在block中對View添加約束。view相對父View的邊距為edge。

代碼簡單,邏輯一目了然。而且還能跟父View一起調(diào)整。簡直是perfect,初見Masonry,驚為天人

細(xì)品

cloudkite給Autolayout披上一層漂亮的外衣之后,將其稱為Masonry,但Masonry的本質(zhì)還是Autolayout。

Autolayout是什么呢?Autolayout就是給View添加一堆約束,讓View在布局的時候通過約束計算出Frame,然后進行布局(Autolayout更多內(nèi)容見Autolayout的第一次親密接觸)。make.edges.equalTo(view.superview).insets(edge);就是添加約束的過程。

對于一個約束。他實際表示的是一個不等或者相等關(guān)系

用Masonry創(chuàng)建一個完整的約束應(yīng)該是這樣的

//view1的左邊距離父View左邊10個點:[view1 mas_makeConstraints:^(MASConstraintMaker *make) {? ? make.left.equalTo(view1.superview.mas_left).multipliedBy(1).offset(10);}];

對應(yīng)到上圖的表達式:

Item1: make MASConstraintMaker類型,view1的承載對象,表示View1

Attribute: left 表示左邊。left的make的屬性。返回值為MASConstraint類型

Relationship: equalTo 表示"="。equalTo是MASConstraint的屬性

Item2: view1.superview

Attribute2: mas_left 同樣表示左邊,mas_left是Masonry給view加的屬性,為了不重名,加了mas前綴

Multiplier: multipliedBy(1) 系數(shù)為1

Constant: offset(10) 常數(shù)為10

Attribute

MASConstraintMaker

上面的表達式中,我們可以看到,make是MASConstraintMaker類型。MASConstraintMaker給我們提供了22種Attribute類型

//Basic Attribute@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;//Margin Attribute@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;//Convenient Attribute@property(nonatomic,strong,readonly) MASConstraint *edges;@property(nonatomic,strong,readonly) MASConstraint *size;@property(nonatomic,strong,readonly) MASConstraint *center;

Attribute總體來說分為三大類

Basic Attribute: 基本屬性,支持到iOS6,一般使用得比較多

Margin Attribute: 邊緣相關(guān)屬性,支持到iOS8。由于版本要求比較高,一般用得比較少。Margin相關(guān)的詳細(xì)內(nèi)容請參考iOS8上關(guān)于UIView的Margin新增了3個APIs

Convenient Attribute: 便捷屬性,為了使用方便而特意新增的屬性。Autolayout本身沒有對應(yīng)的相關(guān)屬性

Convenient Attribute實際是基本屬性的組合。比如:edges表示left, right, top, bottom。

下面的兩個代碼實際的意義是一樣的

//Convenient Attributemake.edges.insets(edge);//Basic Attributemake.left.right.top.bottom.insets(edge);

MASConstraint

前面我們看到MASConstraintMaker中所有的Attribute都是MASConstraint類型。對于多個Attribute一起寫的表達式:

make.left.right.top.bottom.insets(edge);

make.left返回的已經(jīng)是MASConstraint類型,也就是說right這個Attribute是MASConstraint的屬性。

MASConstraint給我們提供了19種Attribute:

//Basic Attribute@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;//Margin Attribute@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;

細(xì)看一下,MASConstraint中的Attribute和MASConstraintMaker完全一樣。只是MASConstraintMaker中多了3種Convenient Attribute。

兩者Attribute的一致,大大的提升了使用的方便性。使用過程中我們不用再去區(qū)分當(dāng)前屬性是MASConstraint還是MASConstraintMaker類型。(事實上沒研究他的類型之前,我都不知道他們分別屬于2種不同類的屬性)

UIView(12月7日新增)

我們可以看到在.equalTo(view1.superview.mas_left)里面,superView也有Attribute。我們來看看UIView中有哪些Attribute:

// Basic Attribute@property(nonatomic,strong,readonly) MASViewAttribute *mas_left;@property(nonatomic,strong,readonly) MASViewAttribute *mas_top;@property(nonatomic,strong,readonly) MASViewAttribute *mas_right;@property(nonatomic,strong,readonly) MASViewAttribute *mas_bottom;@property(nonatomic,strong,readonly) MASViewAttribute *mas_leading;@property(nonatomic,strong,readonly) MASViewAttribute *mas_trailing;@property(nonatomic,strong,readonly) MASViewAttribute *mas_width;@property(nonatomic,strong,readonly) MASViewAttribute *mas_height;@property(nonatomic,strong,readonly) MASViewAttribute *mas_centerX;@property(nonatomic,strong,readonly) MASViewAttribute *mas_centerY;@property(nonatomic,strong,readonly) MASViewAttribute *mas_baseline;// Margin Attribute@property(nonatomic,strong,readonly) MASViewAttribute *mas_leftMargin;@property(nonatomic,strong,readonly) MASViewAttribute *mas_rightMargin;@property(nonatomic,strong,readonly) MASViewAttribute *mas_topMargin;@property(nonatomic,strong,readonly) MASViewAttribute *mas_bottomMargin;@property(nonatomic,strong,readonly) MASViewAttribute *mas_leadingMargin;@property(nonatomic,strong,readonly) MASViewAttribute *mas_trailingMargin;@property(nonatomic,strong,readonly) MASViewAttribute *mas_centerXWithinMargins;@property(nonatomic,strong,readonly) MASViewAttribute *mas_centerYWithinMargins;

可以看出,在UIView中的Attribute和MASConstraint中的幾乎一模一樣,只是每一個Attribute加了一個mas_前綴。

由于UIView是系統(tǒng)的類,對其擴展屬性和方法一般都需要添加自己的前綴,避免跟原有屬性和方法沖突。不過他們的意義跟MASConstraint中的Attribute是相同的

Relationship

約束表示的是2個item之間的關(guān)系,在Autolayout中一共定義了3種關(guān)系:=, >=, <=,對應(yīng)到Masonry中:

- (MASConstraint * (^)(idattr))equalTo;- (MASConstraint * (^)(idattr))greaterThanOrEqualTo;- (MASConstraint * (^)(idattr))lessThanOrEqualTo;

相等關(guān)系我們一般用的多。那么不相等關(guān)系我們什么時候用呢?

假如我有一個Label。Label的長度不能超出父View,如果label中的文字比較短,我希望是文字有多長,Label就有多長。

由于label具有IntrinsicContentSize屬性。所以默認(rèn)情況下,他是文字有多長,Label就有多長。(更多IntrinsicContentSize的內(nèi)容參見Autolayout的第一次親密接觸)。所以我們只需要設(shè)置Label的長度小于父View即可

[label mas_makeConstraints:^(MASConstraintMaker *make) {

? ? make.left.offset(0);

? ? make.centerY.offset(0);

? ? make.width.lessThanOrEqualTo(label.superview);

}];

multiplier

multiplier表示Attribute前面的乘數(shù)。Masonry提供了2種添加multiplier的方法

//? Sets the NSLayoutConstraint multiplier property- (MASConstraint * (^)(CGFloatmultiplier))multipliedBy;//? Sets the NSLayoutConstraint multiplier to 1.0/dividedBy- (MASConstraint * (^)(CGFloatdivider))dividedBy;

multipliedBy: 直接設(shè)置乘數(shù)

dividedBy: 設(shè)置乘數(shù)的倒數(shù) multiplier = 1.0/dividedBy

一般寬或者高的約束使用multiplier比較多

Constant

Masonry提供了4種設(shè)置constant的方法

//Modifies the NSLayoutConstraint constant,only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight- (MASConstraint * (^)(MASEdgeInsets insets))insets;//Modifies the NSLayoutConstraint constant,only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following NSLayoutAttributeWidth, NSLayoutAttributeHeight- (MASConstraint * (^)(CGSizeoffset))sizeOffset;//Modifies the NSLayoutConstraint constant, only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following NSLayoutAttributeCenterX, NSLayoutAttributeCenterY- (MASConstraint * (^)(CGPointoffset))centerOffset;//Modifies the NSLayoutConstraint constant- (MASConstraint * (^)(CGFloatoffset))offset;

insets: 用來設(shè)置left, right, top, bottom。接受MASEdgeInsets類型值

sizeOffset: 用來設(shè)置width, height。接受CGSize類型的值

centerOffset: 用來設(shè)置centerX, centerY。接受CGPoint類型的值

offset: 可以用來設(shè)置所有的東西。接受CGFloat類型的值

其實一般情況下,我只使用offset....

小技巧

如果等式2邊的Attribute是一樣的,我們可以省略等式右邊的Attribute

如果是等于關(guān)系,并且右邊的view是父View。連equalTo也可以省略

如果equalTo里面?zhèn)鞯氖荖SValue類型,效果跟設(shè)置offset是一樣的

如果offset為0,其實也是可以省略的...

下面所有代碼實際效果是一樣的:

// 完整的make.left.equalTo(view1.superview.mas_left).offset(0);//省略Attribute的make.left.equalTo(view1.superview).offset(0);//省略equalTo的make.left.offset(0);//使用equalTo替代offset的make.left.equalTo(@0);//終極大招,省略所有的... 可惜會有warningmake.left;

不過對于make.left,編譯器會報一個警告:你用getter方法獲取回來的值未使用,所以不應(yīng)該使用"."語法

對于這個警告我們可以將返回值轉(zhuǎn)為空消除:

(void)make.left;

不過終究又變得麻煩了,又要多寫6個字母,愁人...

設(shè)置或更新約束

對于約束的設(shè)置,Masonry提供了3種方法,分別為設(shè)置約束、更新約束、重寫設(shè)置約束

// 設(shè)置約束 - (NSArray*)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block;// 更新約束- (NSArray*)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block;// 重新設(shè)置約束- (NSArray*)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block;

mas_makeConstraints: 初次設(shè)置約束使用。

mas_updateConstraints: 更新約束時使用。如果找不著這條約束,會新增,相當(dāng)于mas_makeConstraints。

mas_remakeConstraints: 重新設(shè)置約束。先將view上所有約束移除,再新增約束

注意:mas_updateConstraints只能更新已有約束。如果第一次使用的是left, right設(shè)置的相對寬度。更新的時候想換成使用width。不能使用mas_updateConstraints,因為已有約束里面沒有width的約束,新增width之后會跟原有l(wèi)eft, right約束沖突。此時應(yīng)該使用mas_remakeConstraints

批量設(shè)置約束

假設(shè)有View1,view2,view3三個View,我們想要他們的寬高都等于CGSizeMake(100, 50)。我們可以對他們進行批量設(shè)置:

NSValue*sizeValue = [NSValuevalueWithCGSize:CGSizeMake(100,50)];[@[view1,view2,view3] mas_makeConstraints:^(MASConstraintMaker *make) {? ? make.size.equalTo(sizeValue);}];

由于我們還要設(shè)置view的top,left等位置約束。那可不可以在設(shè)置位置的mas_makeConstraints里面批量設(shè)置寬高呢?實際是可以的!

//advance set[view1 mas_makeConstraints:^(MASConstraintMaker *make) {? ? (void)make.top.left;? ? make.size.equalTo(@[view2,view3,sizeValue]);}];

不過需要注意的是。設(shè)置約束的時候,view一定是已經(jīng)被addSubview的(詳情參考Autolayout的第一次親密接觸),否則會拋異常。所以我們一般在最后一個view上加批量約束

Priority

我們知道約束是有優(yōu)先級的,Masonry給我們提供了4個設(shè)置優(yōu)先級的接口:

// Sets the NSLayoutConstraint priority to a float or MASLayoutPriority- (MASConstraint * (^)(MASLayoutPriority priority))priority;//? Sets the NSLayoutConstraint priority to MASLayoutPriorityLow- (MASConstraint * (^)())priorityLow;//? Sets the NSLayoutConstraint priority to MASLayoutPriorityMedium- (MASConstraint * (^)())priorityMedium;//? Sets the NSLayoutConstraint priority to MASLayoutPriorityHigh- (MASConstraint * (^)())priorityHigh;

priority: 可以設(shè)置任意的優(yōu)先級,接受的參數(shù)是0-1000的數(shù)字

priorityLow: 設(shè)置低優(yōu)先級,優(yōu)先級為250

priorityMedium: 設(shè)置中優(yōu)先級,優(yōu)先級為500

priorityHigh: 設(shè)置高優(yōu)先級,優(yōu)先級為750

需要注意的是,使用priorityLow、priorityMedium、priorityHigh的時候。不是.priorityHigh,而是.priorityHigh()

key

當(dāng)約束沖突發(fā)生的時候,我們經(jīng)常為找不到是哪個View沖突的而煩惱,這一堆View是個什么東西呀?

"","","",""Will attempt to recover by breaking constraint

這時候我們可以設(shè)置View的key:

self.view.mas_key =@"self.view";view1.mas_key =@"view1";

設(shè)置之后再看一下,哈哈,現(xiàn)在好多了??梢郧逦闹朗悄膫€view了

"","","",""Will attempt to recover by breaking constraint

大家可能會覺得這樣一個一個設(shè)置,多麻煩??!別著急,Masonry提供了批量設(shè)置的宏MASAttachKeys

只需要一句代碼即可全部設(shè)置:

MASAttachKeys(self.view,view1);

Shorthand(12月7日新增)

在寫代碼的時候,可能你會感覺有的東西要加mas_前綴,有的東西又不用加,代碼風(fēng)格不統(tǒng)一,而且加mas_前綴還麻煩。

前面介紹過加mas_前綴主要是在擴展系統(tǒng)類的時候為了避免與原有類沖突,這是Apple推薦的做法。不過目前來說,即使不加mas_前綴,也不會有什么問題。所以Masonry提供了不加mas_前綴的方法,只需要你定義幾個宏即可。

MAS_SHORTHAND

定義MAS_SHORTHAND宏之后??梢允褂肬IView,NSArray中不帶mas_前綴的makeConstraints,updateConstraints,remakeConstraints。以及UIView中不帶mas_前綴的Attribute。

MAS_SHORTHAND_GLOBALS

默認(rèn)的equalTo方法只接受id類型的對象。有時候我們想傳入一個CGFloat, CGSize, UIEdgeInsets等。還需要將其轉(zhuǎn)化成NSValue對象,比較麻煩。Masonry也考慮到了這種情況。只需要定義MAS_SHORTHAND_GLOBALS宏。就可以直接對equalTo傳入基礎(chǔ)類型。Masonry自動轉(zhuǎn)化成NSValue對象

撥開Masonry的衣服

Masonry的基本使用方法介紹完了,那么我們來看看Masonry的內(nèi)部到底有些什么東西?

結(jié)構(gòu)

Masonry一共有十三個類,我將這13個類分為5個模塊:

Help

Help模塊主要是一些輔助的類。

NSLayoutConstraint+MASDebugAdditions:這個類的主要作用是重寫NSLayoutConstraint的description函數(shù)。讓約束發(fā)生沖突的時候,更易讀。如果View或者constraint設(shè)置了Key,直接用key的值顯示到description中。如果沒有設(shè)置,顯示View或者constraint的指針。

ViewController+MASAdditions:提供了ViewController的LayoutGuide相關(guān)屬性,以便View對齊時使用

MASUtilities:定義了一些公用的宏和屬性

Shorthand

對于系統(tǒng)原有類(NSArray,UIView)的擴展。Masonry的category方法和屬性都加有mas_前綴。這也是Apple建議的做法,避免跟系統(tǒng)原有方法沖突。但是有時候我們可能想用的更方便,不想寫mas_前綴(沒辦法,我就是這么懶...)

在NSArray+MASShorthandAdditions和View+MASShorthandAdditions中定義了不帶mas_前綴的擴展。這些擴展根據(jù)你是否定義了MAS_SHORTHAND宏來確定是否編譯。所以你只需要定義MAS_SHORTHAND宏,就可以方便的使用不帶mas_前綴的方法,比如:-[view makeConstraints:]

Public

Public模塊主要是對外暴露的方法。使用者使用Masonry可以直接接觸到。

NSArray+MASAdditions:主要有定義和更新約束的方法,如mas_makeConstraints:

View+MASAdditions:除了定義和更新約束的一系列方法之外,還為View增加了mas_top, mas_left等Attribute屬性

Core

Core模塊就是Masonry的核心部分,Masonry的大部分功能都在這4個類里實現(xiàn)

MASConstraintMaker:約束控制器??刂聘?,刪除,或者新增約束

MASConstraint:約束的基類,虛類。定義了Constraint的基本屬性和方法。

MASViewConstraint: 約束的主要實現(xiàn)類。所有對約束使用的功能均在此類中完成

MASCompositeConstraint:約束的集合類。內(nèi)部有一個數(shù)組,可以保存多個MASViewConstraint。對MASCompositeConstraint調(diào)用方法實際等于對其內(nèi)部的所有MASViewConstraint調(diào)用方法

Property

此模塊主要封裝了一些MASConstraint持有的屬性。為了使用更方便,或者擴展功能

MASViewAttribute:每一個Attribute都有一個View與之對應(yīng),為了使用更方便,所以將他們通過一個類封裝在一起

MASLayoutConstraint:默認(rèn)的NSLayoutConstraint是沒有Key這個屬性的,為了Debug方便。派生一個子類,持有key屬性

實現(xiàn)

當(dāng)我們給View添加一個約束的時候到底發(fā)生了什么?

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {

? ? make.left.top.equalTo(view1.superview).offset(20);

}];

我們首先來看make.left.top.equalTo(view1.superview).offset(20);

一、執(zhí)行"make.left"

MASConstraintMaker類中有一個屬性constraints專門用來存儲constraint

@property(nonatomic,strong)NSMutableArray*constraints;

當(dāng)執(zhí)行make.left的時候, 會將相應(yīng)的MASConstraint添加到constraints數(shù)組中

- (MASConstraint *)left {return[selfaddConstraintWithLayoutAttribute:NSLayoutAttributeLeft];}- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {return[selfconstraint:niladdConstraintWithLayoutAttribute:layoutAttribute];}//核心方法- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {? ? MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];? ? MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];//調(diào)用make.left.top時走入這里將原來的ViewConstraint替換成MASCompositeConstraintif([constraint isKindOfClass:MASViewConstraint.class]) {//replace with composite constraintNSArray*children = @[constraint, newConstraint];? ? ? ? MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];? ? ? ? compositeConstraint.delegate =self;? ? ? ? [selfconstraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];returncompositeConstraint;? ? }// 調(diào)用make.left的時候走入這里,將constraint加入到self.constraints中if(!constraint) {? ? ? ? newConstraint.delegate =self;? ? ? ? [self.constraints addObject:newConstraint];? ? }returnnewConstraint;}

對MASConstraintMaker調(diào)用Attribute的get方法,最終都會走到-constraint:addConstraintWithLayoutAttribute:中,在這個方法中,通過對應(yīng)的Attribute生成MASViewConstraint。然后將MASViewConstraint加入到constraints中

二、執(zhí)行".top"

make.left返回的是MASConstraint類型。所以make.left.top是對MASViewConstraint類型調(diào)用top方法。

- (MASConstraint *)top {return[selfaddConstraintWithLayoutAttribute:NSLayoutAttributeTop];}- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {NSAssert(!self.hasLayoutRelation,@"Attributes should be chained before defining the constraint relation");return[self.delegate constraint:selfaddConstraintWithLayoutAttribute:layoutAttribute];}

當(dāng)執(zhí)行-addConstraintWithLayoutAttribute的時候,ViewConstraint通過delegate又調(diào)回到MASConstraintMaker的-constraint:addConstraintWithLayoutAttribute:中。

在MASConstraintMaker的-constraint:addConstraintWithLayoutAttribute:里,將原來constraints中的MASViewConstraint替換成MASCompositeConstraint。MASCompositeConstraint持有top,left 2個屬性。對MASCompositeConstraint做操作時候,其內(nèi)部的所有屬性都會執(zhí)行相應(yīng)的操作

三、執(zhí)行".equalTo(view1.superview)"

- (MASConstraint * (^)(id))equalTo {return^id(idattribute) {returnself.equalToWithRelation(attribute,NSLayoutRelationEqual);? ? };}- (MASConstraint * (^)(id,NSLayoutRelation))equalToWithRelation {return^id(idattribute,NSLayoutRelationrelation) {if([attribute isKindOfClass:NSArray.class]) {NSAssert(!self.hasLayoutRelation,@"Redefinition of constraint relation");NSMutableArray*children =NSMutableArray.new;for(idattrinattribute) {? ? ? ? ? ? ? ? MASViewConstraint *viewConstraint = [selfcopy];? ? ? ? ? ? ? ? viewConstraint.secondViewAttribute = attr;? ? ? ? ? ? ? ? [children addObject:viewConstraint];? ? ? ? ? ? }? ? ? ? ? ? MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];? ? ? ? ? ? compositeConstraint.delegate =self.delegate;? ? ? ? ? ? [self.delegate constraint:selfshouldBeReplacedWithConstraint:compositeConstraint];returncompositeConstraint;? ? ? ? }else{NSAssert(!self.hasLayoutRelation ||self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class],@"Redefinition of constraint relation");self.layoutRelation = relation;self.secondViewAttribute = attribute;returnself;? ? ? ? }? ? };}

當(dāng)執(zhí)行Relationship的方法時,都會走到-equalToWithRelation中。

在這個方法里面主要是給realationship和secondViewAttribute賦值:

如果不是數(shù)組,直接對realationship和secondViewAttribute賦值

如果是數(shù)組,如:.equalTo(@[view1.mas_left,view2.mas_left]),邏輯上肯定不能是不等關(guān)系(>=,<=),所以realationship不用賦值,使用默認(rèn)值(=)。copy出多個viewConstraint,將secondViewAttribute賦值。然后用多個viewConstraint組成的compositeConstraint替換調(diào)原來的viewConstraint。

四、執(zhí)行".offset(10)"

- (MASConstraint * (^)(CGFloat))offset {return^id(CGFloatoffset){self.offset = offset;returnself;? ? };}- (void)setOffset:(CGFloat)offset {self.layoutConstant = offset;}

offset(10)會將10傳入到ViewConstraint中,用layoutConstant屬性將其存起來。(offset主要影響的是約束里面的constant)

五、mas_makeConstraints

看完了make.left.top.equalTo(view1.superview).offset(20);,我們再看看mas_makeConstraints中到底做了什么?

- (NSArray*)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {self.translatesAutoresizingMaskIntoConstraints =NO;? ? MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];? ? block(constraintMaker);return[constraintMaker install];}

mas_makeConstraints方法很簡單,

將self.translatesAutoresizingMaskIntoConstraints至為NO。translatesAutoresizingMaskIntoConstraints表示是否將設(shè)置的Frame轉(zhuǎn)化為約束。當(dāng)自己設(shè)置約束的時候需要將其置為NO

創(chuàng)建出MASConstraintMaker對象

通過block拋出到外面設(shè)值

constraintMaker install

上面的代碼我們知道,關(guān)鍵的地方還是在于constraintMaker install

六、constraintMaker install

- (NSArray*)install {if(self.removeExisting) {NSArray*installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];for(MASConstraint *constraintininstalledConstraints) {? ? ? ? ? ? [constraint uninstall];? ? ? ? }? ? }NSArray*constraints =self.constraints.copy;for(MASConstraint *constraintinconstraints) {? ? ? ? constraint.updateExisting =self.updateExisting;? ? ? ? [constraint install];? ? }? ? [self.constraints removeAllObjects];returnconstraints;}

如果需要removeExisting,就把已有的約束remove掉,當(dāng)調(diào)用mas_remakeConstraints的時候會將removeExisting值置為YES

遍歷constraints,調(diào)用[constraint install]

清空constraints,這里的constraintMaker只是一個零時屬性,只是一個工具類,不需要存儲。所以用完之后就可以將constraints清空

其實真正關(guān)鍵的地方在[constraint install]

七、constraint install

- (void)install {// 1. 已經(jīng)installed的將不做任何操作if(self.hasBeenInstalled) {return;? ? }//2. 從ViewAttribute中剝離出item和attributeMAS_VIEW *firstLayoutItem =self.firstViewAttribute.item;NSLayoutAttributefirstLayoutAttribute =self.firstViewAttribute.layoutAttribute;? ? MAS_VIEW *secondLayoutItem =self.secondViewAttribute.item;NSLayoutAttributesecondLayoutAttribute =self.secondViewAttribute.layoutAttribute;//3. 如果沒有secondViewAttribute,默認(rèn)secondItem為其父View,secontAttribute等于firstLayoutAttribute。if(!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {? ? ? ? secondLayoutItem =self.firstViewAttribute.view.superview;? ? ? ? secondLayoutAttribute = firstLayoutAttribute;? ? }//4. 創(chuàng)建真正用于Autolayout的約束layoutConstraintMASLayoutConstraint *layoutConstraint? ? ? ? = [MASLayoutConstraint constraintWithItem:firstLayoutItem? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? attribute:firstLayoutAttribute? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? relatedBy:self.layoutRelation? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? toItem:secondLayoutItem? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? attribute:secondLayoutAttribute? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? multiplier:self.layoutMultiplier? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? constant:self.layoutConstant];//5. 將priority和key賦值layoutConstraint.priority =self.layoutPriority;? ? layoutConstraint.mas_key =self.mas_key;//6. 找到要添加約束的installViewif(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;? ? }elseif(self.firstViewAttribute.isSizeAttribute) {self.installedView =self.firstViewAttribute.view;? ? }else{self.installedView =self.firstViewAttribute.view.superview;? ? }//7. 添加或更新約束MASLayoutConstraint *existingConstraint =nil;if(self.updateExisting) {? ? ? ? existingConstraint = [selflayoutConstraintSimilarTo:layoutConstraint];? ? }if(existingConstraint) {// just update the constantexistingConstraint.constant = layoutConstraint.constant;self.layoutConstraint = existingConstraint;? ? }else{? ? ? ? [self.installedView addConstraint:layoutConstraint];self.layoutConstraint = layoutConstraint;? ? ? ? [firstLayoutItem.mas_installedConstraints addObject:self];? ? }}

如果已經(jīng)installed就不做任何操作

從ViewAttribute中剝離出item和attribute。前面我們介紹過MASViewAttribute的類主要是將item和attribute2個屬性封裝在了一起。

secondViewAttribute的值來自于.equalTo(item.attribute)中的item.attribute。當(dāng)我們寫下類似make.left.offset(10);約束的時候,是沒有secondViewAttribute的,這時候默認(rèn)secondItem為其父View,secontAttribute等于firstLayoutAttribute。這就解釋了為什么可以這樣寫make.left.offset(10);

創(chuàng)建真正用于Autolayout的約束layoutConstraint

將priority和key賦值

找到要添加約束的installView。如果是2個View之間的約束,需要尋找這2個View最接近的共同父View。添加約束

添加或更新約束。當(dāng)調(diào)用mas_updateConstraints的時候updateExisting=YES。這時候會查找是否有已經(jīng)存在的約束。有就更新,沒有就添加。如果是mas_makeConstraints或mas_remakeConstraints,則直接添加

Extension

僅僅將代碼結(jié)構(gòu)和基本實現(xiàn)過程解析了一下,更多實現(xiàn)細(xì)節(jié)還需要大家自己去閱讀源碼

說實話,Masonry的代碼寫得真漂亮,不管是代碼格式規(guī)范,還是設(shè)計模式??雌饋砗喼笔且环N享受。建議大家閱讀。

Autolayout的第一次親密接觸也更新了一些東西。沒閱讀過或者閱讀時間比較早的朋友可以再看看~

Reference

Masonry源碼

Autolayout的第一次親密接觸

iOS8上關(guān)于UIView的Margin新增了3個APIs

About me

我的博客

我的微博

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容