Masonry是一個輕量級的佈局框架,它擁有本身的描述語法(採用更優雅的鏈式語法封裝)來自動佈局,具備很好可讀性且同時支持iOS和Max OS X等。
總之,對於側重寫代碼的coder,請你慢慢忘記Frame,喜歡Masonry吧app
MASViewAttribute 以對應的系統類型框架
MASViewAttribute | NSLayoutAttribute |
---|---|
view.mas_left | NSLayoutAttributeLeft |
view.mas_right | NSLayoutAttributeRight |
view.mas_top | NSLayoutAttributeTop |
view.mas_bottom | NSLayoutAttributeBottom |
view.mas_leading | NSLayoutAttributeLeading |
view.mas_trailing | NSLayoutAttributeTrailing |
view.mas_width | NSLayoutAttributeWidth |
view.mas_height | NSLayoutAttributeHeight |
view.mas_centerX | NSLayoutAttributeCenterX |
view.mas_centerY | NSLayoutAttributeCenterY |
view.mas_baseline | NSLayoutAttributeBaseline |
UIViewless
先來一波最爲經常使用的使用方法,你們能夠看一下大體語法,下面會細講使用dom
//分別設置各個相對邊距(superview爲view的父類視圖,下同) make.left.mas_equalTo(superView.mas_left).mas_offset(10); make.right.mas_equalTo(superView.mas_right).mas_offset(-10); make.top.mas_equalTo(superView.mas_top).mas_offset(10); make.bottom.mas_equalTo(superView.mas_bottom).offset(-10); //直接鏈接使用left大於等於某個值 make.left.mas_greaterThanOrEqualTo(10); //設置寬和高 make.width.mas_equalTo(60); make.height.mas_equalTo(60); //.設置center和寬高比 make.center.mas_equalTo(superView); make.width.mas_equalTo(superView).multipliedBy(1.00/3); make.height.mas_equalTo(superView).multipliedBy(0.25); //.關於約束優先級,此處要注意約束衝突的問題,統一約束優先級大的生效 make.left.mas_equalTo(100); make.left.mas_equalTo(view.superview.mas_left).offset(10); make.left.mas_equalTo(20).priority(700); make.left.mas_equalTo(40).priorityHigh(); make.left.mas_equalTo(60).priorityMedium(); make.left.mas_equalTo(80).priorityLow(); //若是你想讓view的(x座標)左邊大於等於label的左邊,如下兩個約束的寫法效果同樣 make.left.greaterThanOrEqualTo(label); make.left.greaterThanOrEqualTo(label.mas_left);
注:約束的鏈式寫法中,不包含其餘相對的view時,默認爲其superview,即make.left.mas_equalTo(100);
等價於make.left.mas_equalTo(view.superview.mas_left).offset(10);
和make.left.mas_equalTo(view.superview).offset(10);
ide
更加便利的約束方法佈局
Masonry提供了一些便利的方法供咱們同時建立多個不一樣的約束,他們被稱爲MASCompositeConstraints,如:動畫
edges:ui
// 使一個view的top, left, bottom, right 等於view2的 make.edges.equalTo(view2); //相對於superviewde上左下右邊距分別爲5,10,15,20 make.edges.equalTo(superview).insets(UIEdgeInsetsMake(5, 10, 15, 20))
size:this
// 使得寬度和高度大於等於 titleLabel make.size.greaterThanOrEqualTo(titleLabel) // 相對於superview寬度大100,高度小50 make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50))
center:
//中心與button1對齊 make.center.equalTo(button1) //水平方向中心相對向左偏移5,豎直方向中心向下偏移10 make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10))
你能夠在約束鏈裏添加相應的view來增長代碼的可讀性:
// 除了top,全部的邊界與superview對齊 make.left.right.and.bottom.equalTo(superview); make.top.equalTo(otherView);
NSNumber
自動佈局容許使用常量去設置寬或高,若是你想經過一個數字設置一個view的最小和最大的width,能夠用equality blocks,以下:
//width >= 200 && width <= 400 make.width.greaterThanOrEqualTo(@200); make.width.lessThanOrEqualTo(@400)
然而自動佈局不容許對齊屬性的約束(如:left,right,centerY等)設置爲常量值,你可使用NSNumber來設置相對於父類view這些約束屬性,如:
// creates view.left = view.superview.left + 10 make.left.lessThanOrEqualTo(@10)
若是你不想使用NSNumber來設置,也能夠用以下結構來建立你的約束,如:
make.top.mas_equalTo(42); make.height.mas_equalTo(20); make.size.mas_equalTo(CGSizeMake(50, 100)); make.edges.mas_equalTo(UIEdgeInsetsMake(10, 0, 10, 0)); make.left.mas_equalTo(view).mas_offset(UIEdgeInsetsMake(10, 0, 10, 0));
NSArray
用數組添加集中不一樣類的約束,如:
make.height.equalTo(@[view1.mas_height, view2.mas_height]); make.height.equalTo(@[view1, view2]); make.left.equalTo(@[view1, @100, view3.right]);
常見約束的各類類型
/** 1.尺寸:width、height、size 2.邊界:left、leading、right、trailing、top、bottom 3.中心點:center、centerX、centerY 4.邊界:edges 5.偏移量:offset、insets、sizeOffset、centerOffset 6.priority()約束優先級(0~1000),multipler乘因數, dividedBy除因數 */
mas_makeConstraints
方法的元素必須 事先 添加到父元素中,例如[self.view addSubView:view]
;mas_equalTo
和equalTo
的區別:
equalTo
:僅支持基本類型;mas_equalTo
:支持類型轉換,支持複雜類型。是對equalTo的封裝。支持CGSize CGPoint NSNumber UIEdgeinsets。如下實現的是相同的效果:make.width.equalTo(@100);
跟make.width.mas_equalTo(100);
mas_equalTo
是一個Macro,比較 值;equalTo
比較View。如下實現的是相同的效果make.bottom.mas_equalTo(ws.view.mas_bottom);
跟make.bottom.equalTo(ws.view);
mas_equalTo
比equalTo
多了類型轉換操做,大多數時候兩個方法是 通用的。可是
mas_equalTo
;equalTo
;(特別的多個屬性時,必須使用equalTo
,例如make.left.and.right.equalTo(self.view)
)去掉mas_前綴,只用equalTo,只須要把下面代碼添加到.prefix文件:
// 只要添加了這個宏,就不用帶mas_前綴(`equalTo`就等價於`mas_equalTo`) #define MAS_SHORTHAND // 對於默認的約束參數自動裝箱 #define MAS_SHORTHAND_GLOBALS // 這個頭文件,必定要放在上面兩個宏的後面 #import "Masonry.h"
注意點方法with
和and
,這兩個方法其實沒有作任何操做,方法只是返回對象自己,這個方法的做用,徹底是爲了可讀性。
make.left.and.right.equalTo(self.view);
和make.left.right.equalTo(self.view);
是徹底同樣的,可是加了and
方法的語法,可讀性更好。
multipliedBy
的使用只能是設置同一個控件的,好比這裏的bottomInnerView
,make.height.mas_equalTo(bottomInnerView.mas_width).multipliedBy(3);
簡化:
[iconView makeConstraints:^(MASConstraintMaker *make){ make.top.equalTo(self.view).with.offset(30); make.left.equalTo(self.view).with.offset(30); make.bottom.equalTo(self.view).with.offset(-30); make.right.equalTo(self.view).with.offset(-30); }]
能夠簡化爲
make.top.left.bottom.and.right.equalTo(self.view).with.insets(UIEdgeInsetsMake(10,10,10,10));
或
make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(30.30.30.30));
其中leading
與left trailing
與right
在正常狀況下是等價的,可是在一些佈局是從右至左時(好比阿拉伯文),則會對調,因此基本能夠不理不用。用left
和right
就好。用leading
或trailing
後就不要用left
或right
,若是混用會出現崩潰。
對label
的約束比必須設置最大的約束寬度;
self.titleLabel.preferredMaxLayoutWidth = w - 100;
由於iOS中原點在左上角因此注意使用offset時注意right和bottom用負數
使用Masonry
不須要設置控件的translatesAutoresizingMaskIntoConstraints
屬性爲NO
,(錯誤觀點:爲防止block
中的循環引用,使用弱引用),由於在這裏block
是局部的引用,block
內部引用self
不會形成循環應用的。(不必的寫法:__weak typeof (self) weakSelf = self;
)
Masonry約束控件出現衝突的問題:當約束衝突發生的時候,咱們能夠設置view
的key
來定位是哪一個view。
好比:
redView.mas_key = @"redView"; greenView.mas_key = @"greenView"; blueView.mas_key = @"blueView";
若是以爲這樣一個個設置比較繁瑣,Masonry
提供了批量設置的宏:
// 一句代碼便可所有設置 MASAttachKeysMASAttachKeys(redView,greenView,blueView);
.priority
容許你指定一個精確的優先級,數值越大優先級越高,最高1000
。
priorityHigh
等價於UILayoutPriorityDefaultHigh
,優先級值爲750
。priorityMedium
介於高優先級和低優先級之間,優先級值在250~750
之間。priorityLow
等價於UILayoutPriorityDefaultLow
,優先級值爲250
。優先級能夠在約束的尾部添加:
make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow(); make.top.equalTo(label.mas_top).with.priority(600);
Greater/Less
通常與Priority
一塊兒使用,爲一個Constraint
設置了Greater/Less
後,調整Priority
。若是Constraint
的Priority
的值越大,程序優先設置它的Constraint
效果。
這個方法只會添加新的約束:
[view makeConstraints:^(MASConstraintMaker *make) { }];
這個方法將會覆蓋之前的某些特定的約束
[view updateConstraints:^(MASConstraintMaker *make) { }];
這個方法會將之前的全部約束刪掉,添加新的約束
``` [view remakeConstraints:^(MASConstraintMaker *make) { }]; ```
要記得將約束寫在updateConstraints
裏面:
- (void) updateConstraints{ }
而後調用下面這一串:
[self setNeedsUpdateConstraints]; [self updateConstraintsIfNeeded]; [self layoutIfNeeded];
不然不會更新。
setNeedsLayout:
setNeedsLayout:
告知頁面須要刷新,可是不會馬上開始更新。執行後會馬上調用layoutSubviews
。layoutIfNeed:
告知頁面佈局馬上更新。因此通常都會和setNeedsLayout
一塊兒使用。若是但願馬上生成新的frame
須要調用此方法。利用這點:通常佈局動畫能夠在更新佈局後直接使用這個方法讓動畫生效。layoutSubViews:
系統重寫佈局setNeedsUpdateConstraints:
告訴須要更新約束,可是不會馬上開始。updateConstraintsIfNeeded:
告知馬上更新約束。updateConstraints:
系統更新約束。有時候,你爲了實現動畫或者移除替換一些約束時,你須要去修改一些已經存在的約束,Masonry提供了一些不一樣的方法去更新約束,你也能夠將多個約束存在數組裏。
References
你能夠持有某個特定的約束,讓其成爲成員變量或者屬性
//設置爲公共或私接口
@property (nonatomic, strong) MASConstraint *topConstraint; ... // 添加約束 [view1 mas_makeConstraints:^(MASConstraintMaker *make) { self.topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top); make.left.equalTo(superview.mas_left).with.offset(padding.left); }]; ... // 而後能夠調用 //該約束移除 [self.topConstraint uninstall]; //從新設置value,最經常使用 self.topConstraint.mas_equalTo(20); //該約束失效 [self.topConstraint deactivate]; //該約束生效 [self.topConstraint activate];
mas_updateConstraints
若是你只是想更新一下view對應的約束,可使用 mas_updateConstraints 方法代替 mas_makeConstraints方法
//這是蘋果推薦的添加或者更新約束的地方
// 在響應setNeedsUpdateConstraints方法時,這個方法會被調用屢次
// 此方法會被UIKit內部調用,或者在你觸發約束更新時調用
- (void)updateConstraints { [self.growingButton mas_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); }]; //調用super [super updateConstraints]; }
mas_remakeConstraints
mas_updateConstraints只是去更新一些約束,然而有些時候修改一些約束值是沒用的,這時候mas_remakeConstraints就能夠派上用場了
mas_remakeConstraints某些程度類似於mas_updateConstraints,但不一樣於mas_updateConstraints去更新約束值,他會移除以前的view的全部約束,而後再去添加約束
- (void)changeButtonPosition { [self.button mas_remakeConstraints:^(MASConstraintMaker *make) { make.size.equalTo(self.buttonSize); if (topLeft) { make.top.and.left.offset(10); } else { make.bottom.and.right.offset(-10); } }];
蘋果官方建議:添加/更新約束在updateConstraints
這個方法內
// 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 //最後必須調用父類的更新約束 [super updateConstraints]; }
貼一個官方說明的例子:
@implementation DIYCustomView - (id)init { self = [super init]; if (!self) return nil; // --- Create your views here --- self.button = [[UIButton alloc] init]; return self; } // tell UIKit that you are using AutoLayout + (BOOL)requiresConstraintBasedLayout { return YES; } // this is Apple's recommended place for adding/updating constraints - (void)updateConstraints { // --- remake/update constraints here [self.button remakeConstraints:^(MASConstraintMaker *make) { make.width.equalTo(@(self.buttonSize.width)); make.height.equalTo(@(self.buttonSize.height)); }]; //according to apple super should be called at end of method [super updateConstraints]; } - (void)didTapButton:(UIButton *)button { // --- Do your changes ie change variables that affect your layout etc --- self.buttonSize = CGSize(200, 200); // tell constraints they need updating [self setNeedsUpdateConstraints]; }
多個(2個以上)控件的等間隔排序顯示
/** * axisType 軸線方向 * fixedSpacing 間隔大小 * fixedItemLength 每一個控件的固定長度/寬度 * leadSpacing 頭部間隔 * tailSpacing 尾部間隔 * */ //1. 等間隔排列 - 多個控件間隔固定,控件長度/寬度變化 - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing; //2. 等間隔排列 - 多個固定大小固定,間隔空隙變化 - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
多行label的約束問題
//建立label self.label = [UILabel new]; self.label.numberOfLines = 0; self.label.lineBreakMode = NSLineBreakByTruncatingTail; self.label.text = @"有的人,沒事時喜歡在朋友圈裏處處點贊,東評論一句西評論一句,比誰都有存在感。等你有事找他了,他就馬上變得很忙,讓你再也找不着。真正的朋友,日常不多聯繫。可一旦你趕上了難處,他會馬上回復你的消息,第一時間站出來幫你。所謂的存在感,不是你有沒有出現,而是你的出現有沒有價值。存在感,不是刷出來的,也不是說出來的。有存在感,未必是要個性鋒芒畢露、甚至鋒利扎人。翩翩君子,溫潤如玉,真正有存在感的人,反而不會刻意去強調他的存在感。他的出現,永遠都恰到好處。我所欣賞的存在感,不是長袖善舞巧言令色,而是對他人的真心關照;不是鋒芒畢露計較勝負,而是讓人相處得舒服;不是時時刻刻聒噪不休,而是關鍵時刻能自告奮勇。別總急着出風頭,但願你能有恰到好處的存在感。"; [self addSubview: self.label]; [self.label makeConstraints:^(MASConstraintMaker *make) { make.left.top.equalTo(10); make.right.equalTo(-10); }]; //添加約束 - (void)layoutSubviews { //1. 執行 [super layoutSubviews]; [super layoutSubviews]; //2. 設置preferredMaxLayoutWidth: 多行label約束的完美解決 self.label.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20; //3. 設置preferredLayoutWidth後,須要再次執行 [super layoutSubviews]; //其實在實際中這步不寫,也不會出錯,官方解釋是說設置preferredLayoutWidth後須要從新計算並佈局界面,因此這步最好執行 [super layoutSubviews]; }
UIScrollView的問題
原理同自動佈局同樣 UIScrollView上添加UIView
UIView上添加須要顯示的控件 UIScrollView滾動高度取決於顯示控件的總高度
對子控件作好約束,可達到控制UIView的大小
//建立滾動視圖 UIScrollView *scrollView = [UIScrollView new]; self.scrollView = scrollView; scrollView.backgroundColor = [UIColor grayColor]; [self addSubview:scrollView]; [self.scrollView makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self); }]; [self setUpContentView]; //添加內容視圖 - (void)setUpContentView { //約束UIScrollView上contentView UIView *contentView = [UIView new]; [self.scrollView addSubview:contentView]; [contentView makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.scrollView); make.width.equalTo(self.scrollView); //此處必填 - 關鍵點 }]; //添加控件到contentView,約束原理與自動佈局相同 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); }]; }