Masonry是iOS開發中常見的視圖約束框架,但是有人對他的使用還是淺嘗輒止,接下來會提出幾點比較少見但是又十分便捷的使用技巧。
mas_greaterThanOrEqualTo
mas_greaterThanOrEqualTo
顧名思義是不直接設置該約束,但是限制該約束不要超出邊界,比如我們想讓UILabel
根據文本自適應長度(或高度)的話,就可以這么使用。
UILabel *textLb = [UILabel new];
textLb.font = [UIFont systemFontOfSize:30];
textLb.textColor = [UIColor redColor];
textLb.text = @"MrYu4";
[self.view addSubview:textLb];
[textLb mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(400);
make.left.mas_equalTo(10);
make.height.mas_equalTo(33);
make.width.mas_greaterThanOrEqualTo(0);
}];
效果圖如下,看得出來文本視圖的寬度跟文字長度正好適配。
除了與width
/height
搭配來約束寬/高之外,也可以與left
/right
約束在邊界。
另外mas_lessThanOrEqualTo
則是相反的功能,但也是相同的用法。
priority
看這段代碼,猜一下textLb
的高度會是比較高還是比較矮。
.......
[textLb mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(400);
make.left.mas_equalTo(10);
make.height.mas_equalTo(100).priorityHigh();
make.height.mas_equalTo(33).priorityMedium();
make.width.mas_greaterThanOrEqualTo(0);
}];
圖中可以看出priorityHigh()
那句代碼優先級更高,在Masonry
中兩個會沖突的約束可以用這三個優先級來手動設定哪個優先級更高
- (MASConstraint * (^)(void))priorityLow;
- (MASConstraint * (^)(void))priorityMedium;
- (MASConstraint * (^)(void))priorityHigh;
(三句話什么意思就不用我多說了吧)
當兩個會沖突的約束同時出現時,其實Masonry
會根據自有邏輯來進行處理,但顯然作為開發我們肯定想這個邏輯規則牢牢把握在自己手上,比如下面這段代碼,width
和right
明顯出現了沖突,實際上這段代碼不互報錯,至於會顯示什么效果,可能會有很多人一時說不上來。
...
[textLb mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(400);
make.left.mas_equalTo(self.view.mas_right).offset(-70);
make.height.mas_equalTo(33);
make.right.mas_equalTo(-5);
make.width.mas_equalTo(100);
}];
這時候我們要做的就是添加上優先級(優先級落后的也要加上),這樣子代碼就會順着我們的想法去走,比如我要求的是寧願超出屏幕也要寬度保證為100,就可以這么寫。
...
make.right.mas_equalTo(-5).priorityMedium();
make.width.mas_equalTo(100).priorityHigh();
父視圖大小適配子視圖的大小
有時候一個view
的大小需要其子視圖來確認(尤其是其子視圖的大小還可能需要等服務器返回數據之后才能確認其大小),這個時候我們其實不需要學習到什么新的方法,只需要在設置父視圖時添加對子視圖的依賴即可。
//此處是父視圖
UIView *blueView = [UIView new];
blueView.backgroundColor = [UIColor blueColor];
//下面是兩個子視圖
UILabel *textLb = [UILabel new];
textLb.font = [UIFont systemFontOfSize:30];
textLb.textColor = [UIColor redColor];
[blueView addSubview:textLb];
[textLb mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(0);
make.left.mas_equalTo(20);//表示其在父視圖左邊距20處,相對來講,父視圖至少左側有20長度的寬度了
make.height.mas_equalTo(33);
make.width.mas_greaterThanOrEqualTo(0);
}];
UILabel *textLb2 = [UILabel new];
textLb2.font = [UIFont systemFontOfSize:30];
textLb2.textColor = [UIColor redColor];
[blueView addSubview:textLb2];
[textLb2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(textLb);
make.left.mas_equalTo(textLb.mas_right);
make.height.mas_equalTo(33);
make.width.mas_greaterThanOrEqualTo(0);
}];
[self.view addSubview:blueView];
[blueView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(400);
make.left.mas_equalTo(12);
make.right.mas_equalTo(textLb2);//關鍵點
make.height.mas_equalTo(70);
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 2), dispatch_get_main_queue(), ^{
//模擬服務器返回數據
textLb.text = @"MrYu4";
textLb2.text = @"服務器返回文字";
});
還有的朋友把子視圖寫在父視圖的類里面,處理方法也是同理的,只需要注意的時候不要把寫好的約束給覆蓋甚至移除掉就好了。
多個view並排(列)等寬/距離的實現
要實現這個功能主要仰仗Masonry
中NSArray+MASAdditions.h
,核心思路就是將所有的view
放入數組中,並將這個數組合並成一個個體來思考。
1、實現相同約束
NSArray+MASAdditions.h
實現了和UIView+MASAdditions.h
同名的方法,當我們想讓數組里的view
各個添加一樣的約束時,直接對數組添加就好。
//@interface NSArray (MASAdditions)
- (NSArray *)mas_makeConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block;
- (NSArray *)mas_updateConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block;
- (NSArray *)mas_remakeConstraints:(void (NS_NOESCAPE ^)(MASConstraintMaker *make))block;
也許大聰明還在用
for
循環來對每個view
添加相同約束,其實上面的方法就是這些大聰明的做法的封裝。- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block { NSMutableArray *constraints = [NSMutableArray array]; for (MAS_VIEW *view in self) { NSAssert([view isKindOfClass:[MAS_VIEW class]], @"All objects in the array must be views"); //注意這個[view mas_makeConstraints:block],還有估計有人還沒注意到兩邊的同名方法其實是有返回值的(返回約束數組) [constraints addObjectsFromArray:[view mas_makeConstraints:block]]; } return constraints; }
2、平均排列
NSArray+MASAdditions.h
還有兩個未提及的方法,此刻可以派上用場了。
/**
* distribute with fixed spacing
*
* @param axisType which axis to distribute items along
* @param fixedSpacing 每個item之間的距離
* @param leadSpacing the spacing before the first item and the container
* @param tailSpacing the spacing after the last item and the container
*/
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
/**
* distribute with fixed item size
*
* @param axisType which axis to distribute items along
* @param fixedItemLength 每個item的長度
* @param leadSpacing the spacing before the first item and the container
* @param tailSpacing the spacing after the last item and the container
*/
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
兩個方法只有一個參數不一樣,也體現了二者方法使用場景的不同:
- item距離固定,item寬度根據父視圖空間適應
- item寬度固定,item間距離根據父視圖空間適應
3、代碼
目標:藍色view
作為父視圖,有紅黃綠各三個view距離固定為10,等寬顯示。
NSArray <UIView *>*views = @[redView,yellowView,greenView];
[views mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.bottom.mas_equalTo(0);
}];
[views mas_distributeViewsAlongAxis:MASAxisTypeHorizontal
withFixedSpacing:10
leadSpacing:0
tailSpacing:0];