Masonry是項(xiàng)目中常見(jiàn)的自動(dòng)布局庫(kù)怀酷,采用鏈?zhǔn)秸Z(yǔ)法封裝蛇券。
常見(jiàn)布局方法
1.添加約束
- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
2.更新約束
- (NSArray *)mas_updateConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
3.刪除以前的約束剃毒,重新約束
- (NSArray *)mas_remakeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
添加約束前需將試圖添加到父試圖,否則將引起崩潰
等間距布局
適合固定視圖洁闰,內(nèi)部沒(méi)有清空原有視圖的布局歉甚,所以數(shù)組內(nèi)視圖變動(dòng)后再調(diào)用該方法更新布局會(huì)引起UI異常。
/**
* distribute with fixed spacing
*
* @param axisType 橫向排列或豎行排列
* @param fixedSpacing 視圖間隔大小
* @param leadSpacing 第一個(gè)視圖距邊緣間隔大小
* @param tailSpacing 最后一個(gè)視圖距邊緣間隔大小
*/
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
withFixedSpacing:(CGFloat)fixedSpacing
leadSpacing:(CGFloat)leadSpacing
tailSpacing:(CGFloat)tailSpacing
示例:
NSArray *arrays = @[redView,blueView,yellowView,brownView];
[arrays mas_distributeViewsAlongAxis:MASAxisTypeHorizontal
withFixedSpacing:10.f
leadSpacing:30.f
tailSpacing:30.f];
[arrays mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top).offset(150.f);
make.height.mas_equalTo(50.f);
}];
等寬布局
適合固定視圖扑眉,內(nèi)部沒(méi)有清空原有視圖的布局纸泄,所以數(shù)組內(nèi)視圖變動(dòng)后再調(diào)用該方法更新布局會(huì)引起UI異常。
/**
* distribute with fixed item size
*
* @param axisType 橫向排列或豎行排列
* @param fixedItemLength 視圖寬或高
* @param leadSpacing 第一個(gè)視圖距邊緣間隔大小
* @param tailSpacing 最后一個(gè)視圖距邊緣間隔大小
*/
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
示例:
[arrays mas_distributeViewsAlongAxis:MASAxisTypeHorizontal
withFixedItemLength:75.f
leadSpacing:30.f
tailSpacing:30.f];
[arrays mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top).offset(150.f);
make.height.mas_equalTo(50.f);
}];
leading腰素、trailing
相較于left聘裁、right能夠自動(dòng)改變文字方向(如阿拉伯語(yǔ))。
間隔布局 - offset
offset常用于設(shè)置兩個(gè)視圖邊緣的間隔弓千,同時(shí)可以在布局完成后動(dòng)態(tài)的改動(dòng)視圖間的間隔大小衡便。
示例:視圖左側(cè)相距頁(yè)面左邊緣20像素。
@property (nonatomic, strong) MASConstraint *redViewLeftConstraint;
#pragma mark - 布局
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top).offset(130.f);
_redViewLeftConstraint = make.left.equalTo(self.view.mas_left).offset(20.f);
make.width.mas_equalTo(180.f);
make.height.mas_equalTo(90.f);
}];
改變視圖左間距(此時(shí)間距值會(huì)直接覆蓋上次設(shè)置的間隔大小,不會(huì)累加)镣陕。
_redViewLeftConstraint.offset(100.f);
示例:視圖右側(cè)相距頁(yè)面右邊緣20像素谴餐。
@property (nonatomic, strong) MASConstraint *redViewRightConstraint;
#pragma mark - 布局
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top).offset(130.f);
_redViewRightConstraint = make.right.equalTo(self.view.mas_right).offset(-20.f);
make.width.mas_equalTo(180.f);
make.height.mas_equalTo(90.f);
}];
改變視圖間距
_redViewRightConstraint.offset(-100.f);
結(jié)論:第二個(gè)視圖相對(duì)第一個(gè)視圖右移時(shí)offset(正數(shù))
,第二個(gè)視圖相對(duì)第一個(gè)視圖左移時(shí)offset(負(fù)數(shù))
呆抑。
equalTo與mas_equalTo區(qū)別
equalTo(value)等于value值岂嗓。
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top).offset(130.f);
make.left.equalTo(self.view.mas_left).offset(20.f);
make.width.mas_equalTo(180.f);
make.height.mas_equalTo(90.f);
}];
redView.text = @"redViewredView";
redView.text = @"redViewredViewredViewredView";
如圖所示:視圖寬度固定180像素,不會(huì)因內(nèi)容的改變而改變理肺。
- 區(qū)別
#define mas_equalTo(...) equalTo(MASBoxValue((__VA_ARGS__)))
#define mas_greaterThanOrEqualTo(...) greaterThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_lessThanOrEqualTo(...) lessThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_offset(...) valueOffset(MASBoxValue((__VA_ARGS__)))
mas_equalTo
只是對(duì)其參數(shù)進(jìn)行了一個(gè)BOX
(裝箱) 操作摄闸,目前支持的類(lèi)型:數(shù)值類(lèi)型(NSNumber)、 點(diǎn)(CGPoint)妹萨、大心暾怼(CGSize)、邊距(UIEdgeInsets)
乎完,而equalTo
這個(gè)方法不會(huì)對(duì)參數(shù)進(jìn)行包裝熏兄。
lessThanOrEqualTo
lessThanOrEqualTo(value)少于或等于value值,常用于內(nèi)容可能動(dòng)態(tài)改變情況树姨。
示例:寬度少于等于180像素摩桶。
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top).offset(130.f);
make.left.equalTo(self.view.mas_left).offset(20.f);
make.width.mas_lessThanOrEqualTo(180.f);
make.height.mas_equalTo(90.f);
}];
redView.text = @"redView";
redView.text = @"redViewredViewredView";
如圖所示,視圖寬度會(huì)隨著內(nèi)容動(dòng)態(tài)改變帽揪,但是最大不超過(guò)180像素硝清,超出內(nèi)容省略展示。
示例:此處紅色視圖右邊緣距父視圖右邊緣間隔大于20像素转晰,但是上節(jié)中介紹第二個(gè)視圖相對(duì)第一個(gè)視圖左移時(shí)offset(負(fù)數(shù))
芦拿,故此處使用lessThanOrEqualTo。
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top).offset(130.f);
make.left.equalTo(self.view.mas_left).offset(20.f);
make.right.lessThanOrEqualTo(self.view.mas_right).offset(-20.f);
make.width.mas_equalTo(180.f);
make.height.mas_equalTo(90.f);
}];
redView.text = @"redViewredView";
greaterThanOrEqualTo
greaterThanOrEqualTo(value)大于或等于value值查邢。
示例:寬度大于等于180像素蔗崎。
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top).offset(130.f);
make.left.equalTo(self.view.mas_left).offset(20.f);
make.width.mas_greaterThanOrEqualTo(180.f);
make.height.mas_equalTo(90.f);
}];
redView.text = @"redView";
redView.text = @"redViewredViewredViewredViewredView";
如圖所示:寬度隨著文字的內(nèi)容改變,但即使文字較少時(shí)寬度也不會(huì)小于180像素扰藕。
比例布局 - multipliedBy缓苛、dividedBy
multipliedBy:屬性表示約束值為約束對(duì)象的乘因數(shù)。
dividedBy:屬性表示約束值為約束對(duì)象的除因數(shù)邓深。
示例:紅色視圖寬180高90未桥,藍(lán)色視圖寬180高為寬度的0.5倍數(shù)。
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top).offset(130.f);
make.left.equalTo(self.view.mas_left).offset(20.f);
make.width.mas_equalTo(180.f);
make.height.mas_equalTo(90.f);
}];
[blueView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top).offset(130.f);
make.right.equalTo(self.view.mas_right).offset(-20.f);;
make.width.mas_equalTo(180.f);
make.height.equalTo(blueView.mas_width).multipliedBy(0.5);
}];
通過(guò)圖片可知兩個(gè)視圖高度相同芥备,而藍(lán)色視圖0.5倍寬度的高度值正是90冬耿。
快速定位約束沖突視圖 - mas_key
Masonry可以通過(guò)給視圖控件設(shè)置key值來(lái)區(qū)分不同的控件,方便出現(xiàn)約束沖突時(shí)快速定位到有約束沖突的視圖门躯。
示例:
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top).offset(130.f);
make.left.equalTo(self.view.mas_left).offset(20.f);
make.right.equalTo(self.view.mas_right).offset(-20.f);
make.width.mas_equalTo(180.f);
make.height.mas_equalTo(90.f);
}];
控制臺(tái)會(huì)提示約束沖突日志如下:
[LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<MASLayoutConstraint:0x6000036ea580 UIView:0x7fcbfe4022f0.left == UIView:0x7fcbfe607e10.left + 20>",
"<MASLayoutConstraint:0x6000036ea5e0 UIView:0x7fcbfe4022f0.right == UIView:0x7fcbfe607e10.right - 20>",
"<MASLayoutConstraint:0x6000036e9ec0 UIView:0x7fcbfe4022f0.width == 180>",
"<NSLayoutConstraint:0x6000031caad0 UIView:0x7fcbfe607e10.width == 414>"
)
Will attempt to recover by breaking constraint
<MASLayoutConstraint:0x6000036e9ec0 UIView:0x7fcbfe4022f0.width == 180>
當(dāng)應(yīng)用中布局比較多或者布局比較相似時(shí)想快速準(zhǔn)確定位到有沖突的視圖比較困難淆党。
redView.mas_key = @"redViewKey";
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top).offset(130.f);
make.left.equalTo(self.view.mas_left).offset(20.f);
make.right.equalTo(self.view.mas_right).offset(-20.f);
make.width.mas_equalTo(180.f);
make.height.mas_equalTo(90.f);
}];
[LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<MASLayoutConstraint:0x6000001ec6c0 UIView:redViewKey.left == UIView:0x7fe3b6602b00.left + 20>",
"<MASLayoutConstraint:0x6000001ec1e0 UIView:redViewKey.right == UIView:0x7fe3b6602b00.right - 20>",
"<MASLayoutConstraint:0x6000001ec720 UIView:redViewKey.width == 180>",
"<NSLayoutConstraint:0x6000006d0910 UIView:0x7fe3b6602b00.width == 414>"
)
Will attempt to recover by breaking constraint
<MASLayoutConstraint:0x6000001ec720 UIView:redViewKey.width == 180>
對(duì)比設(shè)置mas_key前后的沖突日志可以發(fā)現(xiàn)酷师,設(shè)置后日志會(huì)直接顯示沖突視圖的key值(UIView:redViewKey.right == ...
)讶凉,這樣我們就能快速準(zhǔn)確的定位到約束沖突視圖染乌。
優(yōu)先級(jí) - priority
每個(gè)約束都有一個(gè)優(yōu)先級(jí),優(yōu)先級(jí)的范圍是1 ~ 1000懂讯,默認(rèn)創(chuàng)建的約束優(yōu)先級(jí)是MASLayoutPriorityRequired(1000)荷憋。
作用:優(yōu)先實(shí)現(xiàn)優(yōu)先級(jí)高的設(shè)置,發(fā)生沖突時(shí)褐望,放棄優(yōu)先級(jí)低的設(shè)置勒庄。
常見(jiàn)優(yōu)先級(jí)設(shè)置:
- .priority() 設(shè)定一個(gè)明確的優(yōu)先級(jí)的值。
- .priorityHigh() 無(wú)參數(shù)瘫里,使用預(yù)設(shè)的MASLayoutPriorityDefaultHigh(750)值实蔽。
- .priorityMedium(),使用默認(rèn)的MASLayoutPriorityDefaultMedium(500)值谨读。
- .priorityLow()局装,使用默認(rèn)的MASLayoutPriorityDefaultLow(250)值。
如上節(jié)中約束沖突可以通過(guò)設(shè)置優(yōu)先級(jí)解決:
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top).offset(130.f);
make.left.equalTo(self.view.mas_left).offset(20.f).priorityHigh();
make.right.equalTo(self.view.mas_right).offset(-20.f).priority(740);
make.width.mas_equalTo(180.f);
make.height.mas_equalTo(90.f);
}];
priorityHigh()優(yōu)先級(jí)高于priority(740)劳殖,故make.left.equalTo(self.view.mas_left).offset(20.f).priorityHigh()
生效铐尚,結(jié)果如圖所示。
單個(gè)約束的生效與刪除 - uninstall哆姻、install
install:約束生效宣增。
uninstall:約束刪除。
示例:
@property (nonatomic, strong) MASConstraint *redViewLeftConstraint;
@property (nonatomic, strong) MASConstraint *redViewRightConstraint;
#pragma mark - 布局
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top).offset(130.f);
_redViewLeftConstraint = make.left.equalTo(self.view.mas_left).offset(20.f).priorityHigh();
_redViewRightConstraint = make.right.equalTo(self.view.mas_right).offset(-20.f).priority(740);
make.width.mas_equalTo(180.f);
make.height.mas_equalTo(90.f);
}];
#pragma mark - 更改約束
[_redViewLeftConstraint uninstall];
[_redViewRightConstraint install];
如圖所示:使用install矛缨、uninstall更改約束后爹脾,視圖變?yōu)榫嘤疫吘?0像素。
提高可讀性 - with劳景、and
- (MASConstraint *)with {
return self;
}
- (MASConstraint *)and {
return self;
}
查看方法代碼可知兩個(gè)方法沒(méi)有做任何操作誉简,只是返回對(duì)象本身,兩個(gè)方法的作用是為了提高可讀性盟广。
動(dòng)畫(huà)
_redViewRightConstraint.offset(-100.f);
//告知需要更新約束闷串,但不會(huì)立刻開(kāi)始,系統(tǒng)然后調(diào)用updateConstraints
[self.view setNeedsUpdateConstraints];
//告知立刻更新約束筋量,系統(tǒng)立即調(diào)用updateConstraints
[self.view updateConstraintsIfNeeded];
[UIView animateWithDuration:0.4 animations:^{
//告知頁(yè)面立刻刷新烹吵,系統(tǒng)立即調(diào)用updateConstraints
[self.view layoutIfNeeded];
}];
updateConstraints
- 重置/更新約束在這個(gè)方法
(updateConstraints)
內(nèi)
- (void)updateConstraints {
[self.redView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top).offset(130.f);
make.left.equalTo(self.view.mas_left).offset(20.f);
make.width.mas_equalTo(180.f);
make.height.mas_equalTo(90.f);
}];
[super updateConstraints];
}
- 設(shè)置
requiresConstraintBasedLayout
為YES
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
注意
使用Masonry不需要設(shè)置控件的translatesAutoresizingMaskIntoConstraints屬性為NO;
防止block中的循環(huán)引用,使用弱引用(這是錯(cuò)誤觀點(diǎn))桨武,在這里block是局部的引用肋拔,block內(nèi)部引用self不會(huì)造成循環(huán)引用的
__weak typeof (self) weakSelf = self;(沒(méi)必要的寫(xiě)法)