『轉(zhuǎn)』追求Masonry

本文內(nèi)容全部轉(zhuǎn)載自追求Masonry

目錄

『使用』

一铅檩、MASConstraintMaker
二袁勺、MASConstraint
三袱结、UIView(12月7日新增)
四、Relationship
五掌实、multiplier
六、Constant
七捆毫、小技巧
1疲扎、如果等式2邊的Attribute是一樣的,我們可以省略等式右邊的Attribute
2倔毙、如果是等于關(guān)系埃仪,并且右邊的view是父View。連equalTo也可以省略
3陕赃、如果equalTo里面?zhèn)鞯氖荖SValue類型卵蛉,效果跟設(shè)置offset是一樣的
4、如果offset為0么库,其實(shí)也是可以省略的...
八傻丝、設(shè)置或更新約束
九、批量設(shè)置約束
十诉儒、Priority
十一葡缰、key
十二、Shorthand(12月7日新增)

『撥開Masonry的衣服』

一忱反、結(jié)構(gòu)
二泛释、Help
三、Shorthand
四温算、Public
五怜校、Core
六、Property

『實(shí)現(xiàn)』

一注竿、執(zhí)行"make.left"
二茄茁、執(zhí)行".top"
三、執(zhí)行".equalTo(view1.superview)"
四巩割、執(zhí)行".offset(10)"
五裙顽、mas_makeConstraints
1、將self.translatesAutoresizingMaskIntoConstraints至為NO宣谈。translatesAutoresizingMaskIntoConstraints表示是否將設(shè)置的Frame轉(zhuǎn)化為約束愈犹。當(dāng)自己設(shè)置約束的時(shí)候需要將其置為NO
2、創(chuàng)建出MASConstraintMaker對(duì)象
3闻丑、通過block拋出到外面設(shè)值
4甘萧、constraintMaker install
六萝嘁、constraintMaker install
七、constraint install
1扬卷、如果已經(jīng)installed就不做任何操作
2牙言、從ViewAttribute中剝離出item和attribute。前面我們介紹過MASViewAttribute的類主要是將item和attribute2個(gè)屬性封裝在了一起怪得。
3咱枉、secondViewAttribute的值來自于.equalTo(item.attribute)中的item.attribute。當(dāng)我們寫下類似make.left.offset(10);約束的時(shí)候徒恋,是沒有secondViewAttribute的蚕断,這時(shí)候默認(rèn)secondItem為其父View,secontAttribute等于firstLayoutAttribute入挣。這就解釋了為什么可以這樣寫make.left.offset(10);
4亿乳、創(chuàng)建真正用于Autolayout的約束layoutConstraint
5、將priority和key賦值
6径筏、找到要添加約束的installView葛假。如果是2個(gè)View之間的約束,需要尋找這2個(gè)View最接近的共同父View滋恬。添加約束
7聊训、添加或更新約束。當(dāng)調(diào)用mas_updateConstraints的時(shí)候updateExisting=YES恢氯。這時(shí)候會(huì)查找是否有已經(jīng)存在的約束带斑。有就更新,沒有就添加勋拟。如果是mas_makeConstraints或mas_remakeConstraints勋磕,則直接添加

『Extension』

『Reference』


『使用』

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

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

一、MASConstraintMaker

makeMASConstraintMaker類型敢靡。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總體來說分為三大類

1朋凉、Basic Attribute: 基本屬性,支持到iOS6醋安,一般使用得比較多
2、Margin Attribute: 邊緣相關(guān)屬性墓毒,支持到iOS8吓揪。由于版本要求比較高,一般用得比較少所计。Margin相關(guān)的詳細(xì)內(nèi)容請(qǐng)參考iOS8上關(guān)于UIView的Margin新增了3個(gè)APIs
3柠辞、Convenient Attribute: 便捷屬性,為了使用方便而特意新增的屬性主胧。Autolayout本身沒有對(duì)應(yīng)的相關(guān)屬性


二叭首、MASConstraint

前面我們看到MASConstraintMaker中所有的Attribute都是MASConstraint類型习勤。對(duì)于多個(gè)Attribute一起寫的表達(dá)式:

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

make.left返回的已經(jīng)是MASConstraint類型,也就是說right這個(gè)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類型冬阳。(事實(shí)上沒研究他的類型之前蛤虐,我都不知道他們分別屬于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中的幾乎一模一樣氯窍,只是每一個(gè)Attribute加了一個(gè)mas_前綴饲常。

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


四、Relationship

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

- (MASConstraint * (^)(id attr))equalTo;
- (MASConstraint * (^)(id attr))greaterThanOrEqualTo;
- (MASConstraint * (^)(id attr))lessThanOrEqualTo;

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

假如我有一個(gè)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 * (^)(CGFloat multiplier))multipliedBy;

//    Sets the NSLayoutConstraint multiplier to 1.0/dividedBy
- (MASConstraint * (^)(CGFloat divider))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 * (^)(CGSize offset))sizeOffset;

//Modifies the NSLayoutConstraint constant, only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following NSLayoutAttributeCenterX, NSLayoutAttributeCenterY
- (MASConstraint * (^)(CGPoint offset))centerOffset;

//Modifies the NSLayoutConstraint constant
- (MASConstraint * (^)(CGFloat offset))offset;

insets: 用來設(shè)置left, right, top, bottom。接受MASEdgeInsets類型值
sizeOffset: 用來設(shè)置width, height。接受CGSize類型的值
centerOffset: 用來設(shè)置centerX, centerY毯侦。接受CGPoint類型的值
offset: 可以用來設(shè)置所有的東西哭靖。接受CGFloat類型的值

其實(shí)一般情況下,我只使用offset....


七侈离、小技巧

1试幽、如果等式2邊的Attribute是一樣的,我們可以省略等式右邊的Attribute
2霍狰、如果是等于關(guān)系抡草,并且右邊的view是父View。連equalTo也可以省略
3蔗坯、如果equalTo里面?zhèn)鞯氖荖SValue類型康震,效果跟設(shè)置offset是一樣的
4、如果offset為0宾濒,其實(shí)也是可以省略的...

下面所有代碼實(shí)際效果是一樣的:

// 完整的
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);

//終極大招腿短,省略所有的... 可惜會(huì)有warning:你用getter方法獲取回來的值未使用,所以不應(yīng)該使用"."語法
make.left;  

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

(void)make.left;    

八绘梦、設(shè)置或更新約束

對(duì)于約束的設(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: 更新約束時(shí)使用榄棵。如果找不著這條約束凝颇,會(huì)新增,相當(dāng)于mas_makeConstraints疹鳄。
mas_remakeConstraints: 重新設(shè)置約束拧略。先將view上所有約束移除,再新增約束

注意:mas_updateConstraints只能更新已有約束瘪弓。如果第一次使用的是left, right設(shè)置的相對(duì)寬度垫蛆。更新的時(shí)候想換成使用width。不能使用mas_updateConstraints腺怯,因?yàn)橐延屑s束里面沒有width的約束袱饭,新增width之后會(huì)跟原有l(wèi)eft, right約束沖突。此時(shí)應(yīng)該使用mas_remakeConstraints


九呛占、批量設(shè)置約束

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

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

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

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

不過需要注意的是。設(shè)置約束的時(shí)候坠狡,view一定是已經(jīng)被addSubview的继找,否則會(huì)拋異常。所以我們一般在最后一個(gè)view上加批量約束


十逃沿、Priority

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

 //    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)先級(jí),接受的參數(shù)是0-1000的數(shù)字
priorityLow: 設(shè)置低優(yōu)先級(jí)凯亮,優(yōu)先級(jí)為250
priorityMedium: 設(shè)置中優(yōu)先級(jí)边臼,優(yōu)先級(jí)為500
priorityHigh: 設(shè)置高優(yōu)先級(jí),優(yōu)先級(jí)為750

需要注意的是假消,使用priorityLow柠并、priorityMediumpriorityHigh的時(shí)候富拗。不是.priorityHigh臼予,而是.priorityHigh()


十一、key

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

"<MASLayoutConstraint:0x7f8de483fb10 UIView:0x7f8de2f53870.left == UIView:0x7f8de2f586c0.left>",
"<MASLayoutConstraint:0x7f8de4818b50 UIView:0x7f8de2f53870.right == UIView:0x7f8de2f586c0.right>",
"<MASLayoutConstraint:0x7f8de4818870 UIView:0x7f8de2f53870.width == 100>",
"<NSLayoutConstraint:0x7f8de4847e90 UIView:0x7f8de2f586c0.width == 375>"

Will attempt to recover by breaking constraint 
<MASLayoutConstraint:0x7f8de4818870 UIView:0x7f8de2f53870.width == 100>

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

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

設(shè)置之后再看一下创千,哈哈缰雇,現(xiàn)在好多了∏┎停可以清晰的知道是哪個(gè)view了

"<MASLayoutConstraint:0x7fcd98d17c40 UIView:view1.left == UIView:self.view.left>",
"<MASLayoutConstraint:0x7fcd98d2b2c0 UIView:view1.right == UIView:self.view.right>",
"<MASLayoutConstraint:0x7fcd98d2adb0 UIView:view1.width == 100>",
"<NSLayoutConstraint:0x7fcd98e42050 UIView:self.view.width == 375>"

Will attempt to recover by breaking constraint 
<MASLayoutConstraint:0x7fcd98d2adb0 UIView:view1.width == 100>

大家可能會(huì)覺得這樣一個(gè)一個(gè)設(shè)置寓涨,多麻煩啊氯檐!別著急戒良,Masonry提供了批量設(shè)置的宏MASAttachKeys
只需要一句代碼即可全部設(shè)置:

MASAttachKeys(self.view,view1);  

十二、Shorthand(12月7日新增)

在寫代碼的時(shí)候冠摄,可能你會(huì)感覺有的東西要加mas_前綴糯崎,有的東西又不用加,代碼風(fēng)格不統(tǒng)一河泳,而且加mas_前綴還麻煩沃呢。

前面介紹過加mas_前綴主要是在擴(kuò)展系統(tǒng)類的時(shí)候?yàn)榱吮苊馀c原有類沖突,這是Apple推薦的做法拆挥。不過目前來說薄霜,即使不加mas_前綴某抓,也不會(huì)有什么問題。所以Masonry提供了不加mas_前綴的方法惰瓜,只需要你定義幾個(gè)宏即可否副。

1、MAS_SHORTHAND
定義MAS_SHORTHAND宏之后崎坊”纲鳎可以使用UIView,NSArray中不帶mas_前綴的makeConstraints,updateConstraints,remakeConstraints奈揍。以及UIView中不帶mas_前綴的Attribute曲尸。

2、MAS_SHORTHAND_GLOBALS
默認(rèn)的equalTo方法只接受id類型的對(duì)象男翰。有時(shí)候我們想傳入一個(gè)CGFloat, CGSize, UIEdgeInsets等另患。還需要將其轉(zhuǎn)化成NSValue對(duì)象,比較麻煩奏篙。Masonry也考慮到了這種情況柴淘。只需要定義MAS_SHORTHAND_GLOBALS宏。就可以直接對(duì)equalTo傳入基礎(chǔ)類型秘通。Masonry自動(dòng)轉(zhuǎn)化成NSValue對(duì)象


『撥開Masonry的衣服』

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

一肺稀、結(jié)構(gòu)

Masonry一共有十三個(gè)類第股,我將這13個(gè)類分為5個(gè)模塊:

image


二、Help

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

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

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

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


三捻浦、Shorthand

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

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


四朵耕、Public

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

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


五煞檩、Core

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

MASConstraintMaker:約束控制器栅贴≌迮龋控制更新,刪除檐薯,或者新增約束
MASConstraint:約束的基類凝赛,虛類。定義了Constraint的基本屬性和方法坛缕。
MASViewConstraint: 約束的主要實(shí)現(xiàn)類墓猎。所有對(duì)約束使用的功能均在此類中完成
MASCompositeConstraint:約束的集合類。內(nèi)部有一個(gè)數(shù)組赚楚,可以保存多個(gè)MASViewConstraint毙沾。對(duì)MASCompositeConstraint調(diào)用方法實(shí)際等于對(duì)其內(nèi)部的所有MASViewConstraint調(diào)用方法


六、Property

此模塊主要封裝了一些MASConstraint持有的屬性宠页。為了使用更方便左胞,或者擴(kuò)展功能

MASViewAttribute:每一個(gè)Attribute都有一個(gè)View與之對(duì)應(yīng),為了使用更方便举户,所以將他們通過一個(gè)類封裝在一起
MASLayoutConstraint:默認(rèn)的NSLayoutConstraint是沒有Key這個(gè)屬性的烤宙,為了Debug方便。派生一個(gè)子類俭嘁,持有key屬性


『實(shí)現(xiàn)』

當(dāng)我們給View添加一個(gè)約束的時(shí)候到底發(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類中有一個(gè)屬性constraints專門用來存儲(chǔ)constraint

@property (nonatomic, strong) NSMutableArray *constraints;

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

- (MASConstraint *)left {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}

- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    return [self constraint:nil addConstraintWithLayoutAttribute: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時(shí)走入這里將原來的ViewConstraint替換成MASCompositeConstraint
   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;
   }

   // 調(diào)用make.left的時(shí)候走入這里兄淫,將constraint加入到self.constraints中
   if (!constraint) {
       newConstraint.delegate = self;
       [self.constraints addObject:newConstraint];
   }
   return newConstraint;
}

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


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

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

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

    return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
}

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

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


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

- (MASConstraint * (^)(id))equalTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}

- (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.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;
        }
    };
}

當(dāng)執(zhí)行Relationship的方法時(shí),都會(huì)走到-equalToWithRelation中梧奢。
在這個(gè)方法里面主要是給realationshipsecondViewAttribute賦值:

如果不是數(shù)組狱掂,直接對(duì)realationshipsecondViewAttribute賦值
如果是數(shù)組,如:.equalTo(@[view1.mas_left,view2.mas_left])亲轨,邏輯上肯定不能是不等關(guān)系(>=,<=)趋惨,所以realationship不用賦值,使用默認(rèn)值(=)惦蚊。copy出多個(gè)viewConstraint器虾,將secondViewAttribute賦值。然后用多個(gè)viewConstraint組成的compositeConstraint替換調(diào)原來的viewConstraint蹦锋。


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

- (MASConstraint * (^)(CGFloat))offset {
    return ^id(CGFloat offset){
        self.offset = offset;
        return self;
    };
}

- (void)setOffset:(CGFloat)offset {
    self.layoutConstant = offset;
}

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


五挤悉、mas_makeConstraints

看完了make.left.top.equalTo(view1.superview).offset(20);,我們?cè)倏纯?code>mas_makeConstraints中到底做了什么巫湘?

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

mas_makeConstraints方法很簡單装悲,

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

2、創(chuàng)建出MASConstraintMaker對(duì)象

3阅嘶、通過block拋出到外面設(shè)值

4属瓣、constraintMaker install

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


六讯柔、constraintMaker 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;
}

如果需要removeExisting抡蛙,就把已有的約束remove掉,當(dāng)調(diào)用mas_remakeConstraints的時(shí)候會(huì)將removeExisting值置為YES
遍歷constraints魂迄,調(diào)用[constraint install]
清空constraints粗截,這里的constraintMaker只是一個(gè)零時(shí)屬性,只是一個(gè)工具類捣炬,不需要存儲(chǔ)熊昌。所以用完之后就可以將constraints清空
其實(shí)真正關(guān)鍵的地方在[constraint install]


七绽榛、constraint install

- (void)install {
    // 1. 已經(jīng)installed的將不做任何操作
    if (self.hasBeenInstalled) {
        return;
    }

    //2. 從ViewAttribute中剝離出item和attribute
    MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item;
    NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;
    MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item;
    NSLayoutAttribute secondLayoutAttribute = 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的約束layoutConstraint
    MASLayoutConstraint *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. 找到要添加約束的installView
    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;
    }

    //7. 添加或更新約束
    MASLayoutConstraint *existingConstraint = nil;
    if (self.updateExisting) {
        existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];
    }
    if (existingConstraint) {
        // just update the constant
        existingConstraint.constant = layoutConstraint.constant;
        self.layoutConstraint = existingConstraint;
    } else {
        [self.installedView addConstraint:layoutConstraint];
        self.layoutConstraint = layoutConstraint;
        [firstLayoutItem.mas_installedConstraints addObject:self];
    }
}  

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

2昂利、從ViewAttribute中剝離出item和attribute届腐。前面我們介紹過MASViewAttribute的類主要是將item和attribute2個(gè)屬性封裝在了一起。

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

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

5、將priority和key賦值

6寒砖、找到要添加約束的installView赐劣。如果是2個(gè)View之間的約束,需要尋找這2個(gè)View最接近的共同父View哩都。添加約束

7魁兼、添加或更新約束。當(dāng)調(diào)用mas_updateConstraints的時(shí)候updateExisting=YES漠嵌。這時(shí)候會(huì)查找是否有已經(jīng)存在的約束咐汞。有就更新,沒有就添加儒鹿。如果是mas_makeConstraints或mas_remakeConstraints化撕,則直接添加


『Extension』

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

說實(shí)話约炎,Masonry的代碼寫得真漂亮植阴,不管是代碼格式規(guī)范,還是設(shè)計(jì)模式圾浅÷邮郑看起來簡直是一種享受。建議大家閱讀狸捕。

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


『Reference』

Masonry源碼
Autolayout的第一次親密接觸
iOS8上關(guān)于UIView的Margin新增了3個(gè)APIs

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市灸拍,隨后出現(xiàn)的幾起案子魁衙,更是在濱河造成了極大的恐慌报腔,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剖淀,死亡現(xiàn)場(chǎng)離奇詭異纯蛾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)纵隔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門翻诉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人捌刮,你說我怎么就攤上這事碰煌。” “怎么了绅作?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵芦圾,是天一觀的道長。 經(jīng)常有香客問我俄认,道長个少,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任眯杏,我火速辦了婚禮夜焦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘岂贩。我一直安慰自己茫经,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布萎津。 她就那樣靜靜地躺著卸伞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪锉屈。 梳的紋絲不亂的頭發(fā)上瞪慧,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音部念,去河邊找鬼弃酌。 笑死,一個(gè)胖子當(dāng)著我的面吹牛儡炼,可吹牛的內(nèi)容都是我干的妓湘。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼乌询,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼榜贴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤唬党,失蹤者是張志新(化名)和其女友劉穎鹃共,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驶拱,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡霜浴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蓝纲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阴孟。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖税迷,靈堂內(nèi)的尸體忽然破棺而出永丝,到底是詐尸還是另有隱情,我是刑警寧澤箭养,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布慕嚷,位于F島的核電站,受9級(jí)特大地震影響毕泌,放射性物質(zhì)發(fā)生泄漏喝检。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一懈词、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辩诞,春花似錦坎弯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至外永,卻和暖如春崎脉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伯顶。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來泰國打工囚灼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人祭衩。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓灶体,卻偏偏與公主長得像,于是被迫代替她去往敵國和親掐暮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蝎抽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Masonry是一個(gè)輕量級(jí)的布局框架,擁有自己的描述語法路克,采用更優(yōu)雅的鏈?zhǔn)秸Z法封裝自動(dòng)布局樟结,簡潔明了并具有高可讀性...
    3dcc6cf93bb5閱讀 1,765評(píng)論 0 1
  • (一)Masonry介紹 Masonry是一個(gè)輕量級(jí)的布局框架 擁有自己的描述語法 采用更優(yōu)雅的鏈?zhǔn)秸Z法封裝自動(dòng)布...
    木易林1閱讀 2,334評(píng)論 0 3
  • Autolayout就像一個(gè)知情達(dá)理养交,善解人意的好姑娘,可惜長相有點(diǎn)不堪入目瓢宦,所以追求者寥寥無幾碎连。所幸遇到了化妝大...
    小笨狼閱讀 23,977評(píng)論 28 227
  • iOS_autoLayout_Masonry 概述 Masonry是一個(gè)輕量級(jí)的布局框架與更好的包裝AutoLay...
    指尖的跳動(dòng)閱讀 1,162評(píng)論 1 4
  • 一、Masonry介紹 之前我們?cè)谄聊贿m配的章節(jié)中學(xué)習(xí)過AutoLayout的使用刁笙,但那都是在可視化界面上進(jìn)行添加...
    無灃閱讀 2,124評(píng)論 0 1