有感于iOS自動(dòng)布局代碼的冗長(zhǎng)和繁瑣,閑來(lái)無(wú)事,便自己封裝了一下。
寫(xiě)的過(guò)程中我借鑒了NSLayoutAnchor和masonry的一些思路,寫(xiě)了充分的單元測(cè)試用例,并對(duì)框架的性能(耗時(shí)和內(nèi)存占用)做了一個(gè)簡(jiǎn)單評(píng)估。
優(yōu)點(diǎn)
- 簡(jiǎn)單,17KB大小。易用,對(duì)于許多布局,用起來(lái)跟frame差不多
- 支持iOS8和swift
- 速度是masonry的1.7倍,接近系統(tǒng)方法速度,占用內(nèi)存小
- 跟蹤所有約束,不需要額外寫(xiě)成員屬性引用約束
- 充分的單元測(cè)試
- 支持iOS11,相對(duì)safeAreaLayoutGuide的布局約束書(shū)寫(xiě)非常簡(jiǎn)便!

設(shè)置約束
所有約束設(shè)置都遵循系統(tǒng)的公式:
firstItem.firstAttribute {=,<=,>=} secondItem.secondAttribute * multiplier + constant
簡(jiǎn)便寫(xiě)法(適用于NSLayoutRelationEqual、multiplier = 1 且除了寬、高以外的約束的constant = 0的情況)
[self.redView activateConstraints:^{
self.redView.height_attr.constant = 100; // redView的高度 = 100
self.redView.width_attr = self.blueView.width_attr; // redView的寬度 = blueView的寬度
self.redView.top_attr = self.blueView.top_attr; // redView的頂部 = blueView的頂部
self.redView.left_attr = self.blueView.right_attr; // redView的左邊 = blueView的右邊
}];
常規(guī)寫(xiě)法
[self.redView activateConstraints:^{
self.redView.height_attr.constant = 150; // redView的高度 = 150
[self.redView.width_attr equalTo:self.blueView.width_attr constant:50]; // redView的寬度 = blueView的寬度 + 50
[self.redView.top_attr equalTo:self.blueView.top_attr constant:-10]; // redView的頂部 = blueView的頂部 - 10
[self.redView.left_attr greaterThan:self.blueView.right_attr constant:20]; // redView的左邊 >= blueView的右邊 + 20
}];
activateConstraints方法會(huì)將block里所有約束都綁定給調(diào)用者,這里就是self.redView,這么做是為了方便以后獲取約束。
也可以用更接近系統(tǒng)的風(fēng)格設(shè)置約束,但這樣以后想獲取特定的某個(gè)約束就比較麻煩,需要?jiǎng)?chuàng)建一個(gè)成員變量引用以后想改變的約束。
NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:4];
[arrayM addObject:[self.redView.height_attr equalTo:nil constant:100]];
[arrayM addObject:[self.redView.width_attr equalTo:self.blueView.width_attr]];
[arrayM addObject:[self.redView.top_attr equalTo:self.blueView.top_attr]];
[arrayM addObject:[self.redView.left_attr equalTo:self.blueView.right_attr]];
[NSLayoutConstraint activateConstraints:arrayM];
更新safeAreaLayoutGuide布局寫(xiě)法,不需要寫(xiě)版本判斷
[self.redView activateConstraints:^{
self.redView.height_attr.constant = 100; // redView的高度 = 100
self.redView.width_attr = self.blueView.width_attr; // redView的寬度 = blueView的寬度
self.redView.top_attr = self.view.top_attr_safe; // redView的頂部 = view的safeAreaLayoutGuide的頂部,非iOS11則是view的頂部
self.redView.left_attr = self.view.right_attr_safe; // redView的左邊 = view的safeAreaLayoutGuide的右邊,非iOS11則是view的右邊
}];
改變約束值
self.blueView.width_attr.constant = 100;
或者
[self.redView constraintAccordingToAttribute:self.redView.height_attr].constant = 100;
獲取約束
獲取非常方便,當(dāng)初創(chuàng)建約束是怎樣的相對(duì)關(guān)系,通過(guò)相同的關(guān)系就可以獲取約束。
NSLayoutConstraint *cons = [self.titleLabel constraintAccordingToAttribute:self.titleLabel.bottom_attr andAttribute:self.subtitleLabel.top_attr];
激活和關(guān)閉約束
[self.redView activateConstraintAccordingToAttribute:self.redView.height_attr];
[self.redView deactivateConstraintAccordingToAttribute:self.redView.height_attr];
注意:調(diào)用deactivateConstraintAccordingToAttribute會(huì)將該約束對(duì)象銷(xiāo)毀,這里保持跟系統(tǒng)的deactivateConstraint方法一致。如果只是想暫時(shí)關(guān)閉約束,以后想再activate,則應(yīng)該獲取約束后,設(shè)置.active = NO
解釋一下核心方法 -(void)activateConstraints:(void (^)())constraints;
這是UIView的一個(gè)對(duì)象方法,一般來(lái)說(shuō)每個(gè)UIView的布局代碼寫(xiě)在自己的block里, 只有調(diào)一次該方法,才會(huì)給這個(gè)方法的調(diào)用者創(chuàng)建一個(gè)用來(lái)引用布局對(duì)象的可變數(shù)組,以后才可以通過(guò)這個(gè)調(diào)用者拿到里面的布局對(duì)象。
并且只要調(diào)用了一次該方法,以后即使不在該方法的block里寫(xiě)的約束,約束的firstItem如果和該方法的調(diào)用者是一個(gè)對(duì)象,也會(huì)把這個(gè)約束添加到firstItem(調(diào)用者)的數(shù)組里。
如果調(diào)用了activateConstraints后,又希望移除對(duì)應(yīng)view的數(shù)組,可以調(diào)用
- (void)deactivateAllConstraintsAndAssociatedObjects
具體使用請(qǐng)移步github下載示例代碼。
添加到項(xiàng)目
使用CocoaPods
在podfile中加入 'NSLayoutConstraint-SSLayout'
直接使用源代碼
將Source文件夾下的文件copy到項(xiàng)目