簡(jiǎn)介
Masonry is still actively maintained, we are committed to fixing bugs and merging good quality PRs from the wider community. However if you're using Swift in your project, we recommend using SnapKit as it provides better type safety with a simpler API.
使用前的準(zhǔn)備
// 對(duì)于約束參數(shù)可以省去"mas_"
#define MAS_SHORTHAND
// 對(duì)于默認(rèn)的約束參數(shù)自動(dòng)裝箱
#define MAS_SHORTHAND_GLOBALS
直接這樣
// define this constant if you want to use Masonry without the 'mas_' prefix
#define MAS_SHORTHAND
// define this constant if you want to enable auto-boxing for default syntax
#define MAS_SHORTHAND_GLOBALS
// 宏必須添加在頭文件前面
#import "Masonry.h"
添加約束前提:被約束的必須有父控件,其中約束項(xiàng)都必須是
UIView
或子類的實(shí)例。
約束的三種方法
/**
// 這個(gè)方法只會(huì)添加新的約束
[blueView mas_makeConstraints:^(MASConstraintMaker *make) {
}];
// 這個(gè)方法會(huì)將以前的所有約束刪掉,添加新的約束
[blueView mas_remakeConstraints:^(MASConstraintMaker *make) {
}];
// 這個(gè)方法將會(huì)覆蓋以前的某些特定的約束
[blueView mas_updateConstraints:^(MASConstraintMaker *make) {
}];
*/
常見約束的各種類型
/**
尺寸:width袒哥、height枝秤、size
邊界:left、leading反镇、right赏陵、trailing沉帮、top、bottom
中心點(diǎn):center珍剑、centerX掸宛、centerY
邊界:edges
偏移量:offset、insets招拙、sizeOffset唧瘾、centerOffset
priority()約束優(yōu)先級(jí)(0~1000),multipler乘因數(shù), dividedBy除因數(shù)
*/
Masonry約束易忽略的技術(shù)點(diǎn)
使用
Masonry
不需要設(shè)置控件的translatesAutoresizingMaskIntoConstraints
屬性為NO
;
防止block
中的循環(huán)引用别凤,使用弱引用(這是錯(cuò)誤觀點(diǎn))饰序,在這里block
是局部的引用,block
內(nèi)部引用self
不會(huì)造成循環(huán)引用的
__weak typeof (self) weakSelf = self;
Masonry約束控件出現(xiàn)沖突的問題
當(dāng)約束沖突發(fā)生的時(shí)候规哪,我們可以設(shè)置
view
的key
來定位是哪個(gè)view
redView.mas_key = @"redView";
greenView.mas_key = @"greenView";
blueView.mas_key = @"blueView";
若是覺得這樣一個(gè)個(gè)設(shè)置比較繁瑣求豫,怎么辦呢,Masonry
則提供了批量設(shè)置的宏
MASAttachKeys
MASAttachKeys(redView,greenView,blueView);
約束iconView距離各個(gè)邊距為30
[iconView makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(30, 30, 30, 30));
}];
equalTo 和 mas_equalTo的區(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__)))
得出結(jié)論:mas_equalTo
只是對(duì)其參數(shù)進(jìn)行了一個(gè)BOX
(裝箱) 操作诉稍,目前支持的類型:數(shù)值類型(NSNumber
)蝠嘉、 點(diǎn)(CGPoint
)、大斜蕖(CGSize
)蚤告、邊距(UIEdgeInsets
),而equalTo
這個(gè)方法不會(huì)對(duì)參數(shù)進(jìn)行包裝服爷。
[iconView makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.view).offset(-30);
make.top.equalTo(self.view).offset(30);
make.height.width.equalTo(100); //== make.size.equalTo(100);
// make.size.mas_equalTo(self.view).multipliedBy(0.5);
// make.top.greaterThanOrEqualTo(self.view).offset(padding);
}];
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
// with 增強(qiáng)可讀性
make.top.equalTo(superview.mas_top).with.offset(10);
// and 增強(qiáng)可讀性
make.left.equalTo(greenView.mas_right).and.offset(10);
make.bottom.equalTo(blueView.mas_top).offset(-10);
make.right.equalTo(superview.mas_right).offset(-10);
make.width.equalTo(greenView.mas_width);
// 約束參數(shù)相同可以通過數(shù)組
make.height.equalTo(@[greenView, blueView]);
}];
更新約束的問題
- 監(jiān)聽按鈕點(diǎn)擊
[self.btn addTarget:self action:@selector(didClickBtn:) forControlEvents:UIControlEventTouchUpInside];
- 處理事件
- (void)didClickBtn:(UIButton *)button {
// 設(shè)置一個(gè)屬性(btnSize)保存其大小的變化
self.btnSize = CGSizeMake(self.btnSize.width * 1.3, self.btnSize.height * 1.3);
// 告知需要更新約束杜恰,但不會(huì)立刻開始获诈,系統(tǒng)然后調(diào)用updateConstraints
[self setNeedsUpdateConstraints];
// 告知立刻更新約束,系統(tǒng)立即調(diào)用updateConstraints
[self updateConstraintsIfNeeded];
// 這里動(dòng)畫當(dāng)然可以取消箫章,具體看項(xiàng)目的需求
// 系統(tǒng)block內(nèi)引用不會(huì)導(dǎo)致循環(huán)引用烙荷,block結(jié)束就會(huì)釋放引用對(duì)象
[UIView animateWithDuration:0.4 animations:^{
// 告知頁面立刻刷新,系統(tǒng)立即調(diào)用updateConstraints
[self layoutIfNeeded];
}];
}
- 蘋果官方建議:添加/更新約束在這個(gè)方法(updateConstraints)內(nèi)
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
// 更新約束
[self.btn updateConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self);
make.width.equalTo(@(self.buttonSize.width)).priorityLow();
make.height.equalTo(@(self.buttonSize.height)).priorityLow();
make.width.lessThanOrEqualTo(self);
make.height.lessThanOrEqualTo(self);
}];
// according to apple super should be called at end of method
// 最后必須調(diào)用父類的更新約束
[super updateConstraints];
}
- 設(shè)置requiresConstraintBasedLayout為YES
+ (BOOL)requiresConstraintBasedLayout {
// 重寫這個(gè)方法 若視圖基于自動(dòng)布局的
return YES ;
}
重置約束的問題
對(duì)于控件的重新約束檬寂,則之前的約束都是無效的终抽,步驟都更新約束一樣的,只是在updateConstraints
方法內(nèi)的約束方法改為了remakeConstraints
桶至,直接貼代碼吧(仍以按鈕為例昼伴,其他原理都是相同的)
// 首先監(jiān)聽按鈕
[self.btn addTarget:self action:@selector(didClickBtn:) forControlEvents:UIControlEventTouchUpInside];
// 處理事件
- (void)didClickBtn:(UIButton *)button {
(...) // 觸發(fā)條件
[self setNeedsUpdateConstraints];
[self updateConstraintsIfNeeded];
/**
* 動(dòng)畫展示變化 - 這句代碼可有可無,參考項(xiàng)目具體的需求
* [UIView animateWithDuration:0.4 animations:^{
* [self layoutIfNeeded];
* }];
*/
}
// 重置約束
- (void)updateConstraints {
[self.btn remakeConstraints:^(MASConstraintMaker *make) {
.....
}];
[super updateConstraints];
}
+ (BOOL)requiresConstraintBasedLayout{
// 重寫這個(gè)方法 若視圖基于自動(dòng)布局的
return YES ;
}
多個(gè)(2個(gè)以上)控件的等間隔排序顯示
2個(gè)函數(shù)
/**
* axisType 軸線方向
* fixedSpacing 間隔大小
* fixedItemLength 每個(gè)控件的固定長(zhǎng)度/寬度
* leadSpacing 頭部間隔
* tailSpacing 尾部間隔
*
*/
// 等間隔排列 - 多個(gè)控件間隔固定镣屹,控件長(zhǎng)度/寬度變化
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
withFixedSpacing:(CGFloat)fixedSpacing
leadSpacing:(CGFloat)leadSpacing
tailSpacing:(CGFloat)tailSpacing;
// 等間隔排列 - 多個(gè)固定大小固定圃郊,間隔空隙變化
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType
withFixedItemLength:(CGFloat)fixedItemLength
leadSpacing:(CGFloat)leadSpacing
tailSpacing:(CGFloat)tailSpacing;
// 首先添加5個(gè)視圖
NSMutableArray *array = [NSMutableArray new];
for (int i = 0; i < 5; i ++) {
UIView *view = [UIView new];
view.backgroundColor = [UIColor greenColor];
[self addSubview:view];
// 保存添加的控件
[array addObject:view];
}
// 水平方向控件間隔固定等間隔
[array mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:15 leadSpacing:10 tailSpacing:10];
[array makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(50);
make.height.equalTo(70);
}];
// 水平方向?qū)挾裙潭ǖ乳g隔
[array mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedItemLength:70 leadSpacing:10 tailSpacing:10];
[array makeConstraints:^(MASConstraintMaker *make) {
// 數(shù)組額你不必須都是view
make.top.equalTo(50);
make.height.equalTo(70);
}];
多行l(wèi)abel的約束問題
// 創(chuàng)建label
self.label = [UILabel new];
self.label.numberOfLines = 0;
self.label.lineBreakMode = NSLineBreakByTruncatingTail;
self.label.text = @"有的人,沒事時(shí)喜歡在朋友圈里到處點(diǎn)贊女蜈,東評(píng)論一句西評(píng)論一句持舆,比誰都有存在感。等你有事找他了伪窖,他就立刻變得很忙逸寓,讓你再也找不著。真正的朋友覆山,平常很少聯(lián)系竹伸。可一旦你遇上了難處簇宽,他會(huì)立刻回復(fù)你的消息勋篓,第一時(shí)間站出來幫你。所謂的存在感魏割,不是你有沒有出現(xiàn)譬嚣,而是你的出現(xiàn)有沒有價(jià)值。存在感钞它,不是刷出來的拜银,也不是說出來的。有存在感须揣,未必是要個(gè)性鋒芒畢露盐股、甚至鋒利扎人。翩翩君子耻卡,溫潤(rùn)如玉疯汁,真正有存在感的人,反而不會(huì)刻意去強(qiáng)調(diào)他的存在感卵酪。他的出現(xiàn)幌蚊,永遠(yuǎn)都恰到好處谤碳。我所欣賞的存在感,不是長(zhǎng)袖善舞巧言令色溢豆,而是對(duì)他人的真心關(guān)照蜒简;不是鋒芒畢露計(jì)較勝負(fù),而是讓人相處得舒服漩仙;不是時(shí)時(shí)刻刻聒噪不休搓茬,而是關(guān)鍵時(shí)刻能挺身而出。別總急著出風(fēng)頭队他,希望你能有恰到好處的存在感卷仑。";
[self addSubview: self.label];
[self.label makeConstraints:^(MASConstraintMaker *make) {
make.left.top.equalTo(10);
make.right.equalTo(-10);
}];
// 添加約束
- (void)layoutSubviews {
// 執(zhí)行 [super layoutSubviews];
[super layoutSubviews];
// 設(shè)置preferredMaxLayoutWidth: 多行l(wèi)abel約束的完美解決
self.label.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20;
// 設(shè)置preferredLayoutWidth后,需要再次執(zhí)行 [super layoutSubviews];
// 其實(shí)在實(shí)際中這步不寫麸折,也不會(huì)出錯(cuò)锡凝,官方解釋是說設(shè)置preferredLayoutWidth后需要重新計(jì)算并布局界面,所以這步最好執(zhí)行
[super layoutSubviews];
}
UIScrollView
原理同自動(dòng)布局一樣
UIScrollView
上添加UIView
UIView
上添加需要顯示的控件UIScrollView
滾動(dòng)高度取決于顯示控件的總高度
對(duì)子控件做好約束垢啼,可達(dá)到控制UIView
的大小
// 創(chuàng)建滾動(dòng)視圖
UIScrollView *scrollView = [UIScrollView new];
self.scrollView = scrollView;
scrollView.backgroundColor = [UIColor grayColor];
[self addSubview:scrollView];
[self.scrollView makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self);
}];
// 添加內(nèi)容視圖
[self setUpContentView];
- (void)setUpContentView {
// 約束UIScrollView上contentView
UIView *contentView = [UIView new];
[self.scrollView addSubview:contentView];
[contentView makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.scrollView);
// 此處必填 - 關(guān)鍵點(diǎn)
make.width.equalTo(self.scrollView);
}];
// 添加控件到contentView窜锯,約束原理與自動(dòng)布局相同
UIView *lastView;
CGFloat height = 30;
for (int i = 0; i <1 5; i ++) {
UIView *view = UIView.new;
view.backgroundColor = [UIColor colorWithRed:arc4random() % 255 / 256.0 green:arc4random() % 255 / 256.0 blue:arc4random() % 255 / 256.0 alpha:1.0];
[contentView addSubview:view];
[view makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(lastView ? lastView.bottom : @0);
make.left.equalTo(0);
make.width.equalTo(contentView.width);
make.height.equalTo(height);
}];
height += 30;
lastView = view;
}
[contentView makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(lastView.bottom);
}];
}