本文內(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
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總體來說分為三大類
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
柠并、priorityMedium
、priorityHigh
的時(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è)模塊:
二、Help
Help模塊主要是一些輔助的類话原。
NSLayoutConstraint+MASDebugAdditions
:這個(gè)類的主要作用是重寫NSLayoutConstraint
的description
函數(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+MASShorthandAdditions
和View+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è)方法里面主要是給realationship
和secondViewAttribute
賦值:
如果不是數(shù)組狱掂,直接對(duì)realationship
和secondViewAttribute
賦值
如果是數(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