版本記錄
| 版本號 | 時間 |
|---|---|
| V1.0 | 2018.07.15 |
前言
我們做APP界面,也就是布局UI,那么關(guān)于布局,我們有很多方法,蘋果也都提供了支持,市場上我們用的并不是系統(tǒng)提供原生的layout,對于OC語言一般都是使用一個第三方的布局框架 —— Masonry。接下來幾篇我們就一起深入看一下這個框架。
Masonry概覽
Masonry利用簡化,鏈式和富有表現(xiàn)力的語法,利用AutoLayout NSLayoutConstraints的強大功能。 支持iOS和OSX自動布局。
關(guān)于布局,我們一般很少直接用蘋果的原生API進行布局和適配,市場上使用很多的就是OC使用Masonry,如果是swift語言,那么就使用SnapKit。他們是同一個作者,感興趣的可以點擊這里SnapKit/Masonry,可以fork回去自己慢慢研究。
題外話:我工作這幾年唯一的一次見過的就是前兩年在一家小公司工作的時候,leader是百度T6出來的,他就不讓用三方Masonry布局,他讓我們所有人布局都使用Frame,當(dāng)時就很疑惑不知道他就是不習(xí)慣使用三方,認為三方隱患比較多,還是他就是個Low B,我是Get不到他的點。直到后來我去另外一家大廠工作,我問一些其他同事,他們也是很不理解,每個VC里面,打開代碼視圖實例化和布局那個方法里面就最少一二百行,說實話我感覺很惡心,弄的跟Shi一樣,還不讓我們用xib,實在不明白,題外話就說這么多。
框架其中的一個作者如下所示,感興趣的可以Follow他。

Masonry目前仍在積極維護,致力于修復(fù)錯誤并合并來自更廣泛社區(qū)的優(yōu)質(zhì)PR。 但是,如果您在項目中使用Swift,建議使用**SnapKit **,因為它通過更簡單的API提供更好的類型安全性。
Masonry是一個輕量級的布局框架,它使用更好的語法包裝AutoLayout。 Masonry有自己的布局DSL,它提供了一種描述NSLayoutConstraints的可鏈接方式,從而使布局代碼更簡潔,更易讀。 Masonry支持iOS和Mac OS X。
有關(guān)示例,請查看Masonry workspace中的Masonry iOS Examples項目,下載后您需要運行pod install。
What's wrong with NSLayoutConstraints? - NSLayoutConstraints怎么了
Auto Layout是一種強大而靈活的組織和布局視圖的方式。 但是,從代碼創(chuàng)建約束是冗長的,并不是非常具有描述性。 想象一個簡單的例子,你想要一個視圖填充其父視圖,但每邊插入10個像素間隔。
UIView *superview = self.view;
UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[superview addConstraints:@[
//view1 constraints
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:padding.left],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-padding.bottom],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1
constant:-padding.right],
]];
即使有這樣一個簡單的例子,所需的代碼也非常冗長,當(dāng)你有超過2或3個視圖時很快變得不可讀。 另一種選擇是使用視覺格式語言(VFL),這種語言有點不長。 然而,ASCII類型語法有其自身的缺陷,并且它也有點難以動畫,因為NSLayoutConstraint constraintsWithVisualFormat:返回一個數(shù)組。
Prepare to meet your Maker! - 看一下Maker
這里使用MASConstraintMaker創(chuàng)建同樣的約束。
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];
或者,可以更短和簡潔
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
}];
另請注意,在第一個示例中,我們必須將約束添加到superview [superview addConstraints:....但是Masonry會自動將約束添加到適當(dāng)?shù)囊晥D中。
Masonry也會為你調(diào)用view1.translatesAutoresizingMaskIntoConstraints = NO;。
Not all things are created equal - 并不是所有的約束都使用equal
-
.equalTo相當(dāng)于NSLayoutRelationEqual -
.lessThanOrEqualTo相當(dāng)于NSLayoutRelationLessThanOrEqual -
.greaterThanOrEqualTo相當(dāng)于NSLayoutRelationGreaterThanOrEqual
這三個等式約束接受一個可以是以下任何一個的參數(shù):
1. MASViewAttribute
make.centerX.lessThanOrEqualTo(view2.mas_left);

2. UIView/NSView
如果你想view.left大于或等于label.left:
//these two constraints are exactly the same
make.left.greaterThanOrEqualTo(label);
make.left.greaterThanOrEqualTo(label.mas_left);
3. NSNumber
自動布局允許將寬度和高度設(shè)置為常量值。 如果要將視圖設(shè)置為具有最小和最大寬度,則可以將數(shù)字傳遞給equality block:
//width >= 200 && width <= 400
make.width.greaterThanOrEqualTo(@200);
make.width.lessThanOrEqualTo(@400)
但是,“自動布局”不允許將對齊屬性(如left,right,centerY等)設(shè)置為常量值。 因此,如果您為這些屬性傳遞NSNumber,Masonry會將這些變?yōu)橄鄬τ谝晥D的父視圖的約束,即:
//creates view.left = view.superview.left + 10
make.left.lessThanOrEqualTo(@10)
您可以使用基元和結(jié)構(gòu)來構(gòu)建約束,而不是使用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));
默認情況下,支持autoboxing的宏以mas_為前綴。 通過在導(dǎo)入Masonry之前定義MAS_SHORTHAND_GLOBALS,可以使用未加前綴的版本。
4. NSArray
任何先前類型的混合陣列
make.height.equalTo(@[view1.mas_height, view2.mas_height]);
make.height.equalTo(@[view1, view2]);
make.left.equalTo(@[view1, @100, view3.right]);
Learn to prioritize - 學(xué)習(xí)優(yōu)先級
-
.priority允許您指定確切的優(yōu)先級 -
.priorityHigh相當(dāng)于UILayoutPriorityDefaultHigh -
.priorityMedium介于高低之間 -
.priorityLow相當(dāng)于UILayoutPriorityDefaultLow
優(yōu)先級可以固定到約束鏈的末尾,如下所示:
make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow();
make.top.equalTo(label.mas_top).with.priority(600);
Composition, composition, composition
Masonry還為您提供了一些方便的方法,可以同時創(chuàng)建多個約束。 這些被稱為MASCompositeConstraints
edges
// make top, left, bottom, right equal view2
make.edges.equalTo(view2);
// make top = superview.top + 5, left = superview.left + 10,
// bottom = superview.bottom - 15, right = superview.right - 20
make.edges.equalTo(superview).insets(UIEdgeInsetsMake(5, 10, 15, 20))
size
// make width and height greater than or equal to titleLabel
make.size.greaterThanOrEqualTo(titleLabel)
// make width = superview.width + 100, height = superview.height - 50
make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50))
center
// make centerX and centerY = button1
make.center.equalTo(button1)
// make centerX = superview.centerX - 5, centerY = superview.centerY + 10
make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10))
您可以鏈接視圖屬性以提高可讀性:
// All edges but the top should equal those of the superview
make.left.right.and.bottom.equalTo(superview);
make.top.equalTo(otherView);
Hold on for dear life
有時您需要修改現(xiàn)有約束以設(shè)置動畫或刪除/替換約束。 在Masonry中,有一些不同的方法來更新約束。
1. 引用
您可以通過將約束生成表達式的結(jié)果分配給局部變量或類屬性來保留特定約束的引用。 您還可以通過將它們存儲在數(shù)組中來引用多個約束。
// in public/private interface
@property (nonatomic, strong) MASConstraint *topConstraint;
...
// when making constraints
[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);
}];
...
// then later you can call
[self.topConstraint uninstall];
2. mas_updateConstraints
或者,如果您只更新約束的常量值,則可以使用便利方法mas_updateConstraints而不是mas_makeConstraints
// this is Apple's recommended place for adding/updating constraints
// this method can get called multiple times in response to setNeedsUpdateConstraints
// which can be called by UIKit internally or in your code if you need to trigger an update to your constraints
- (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);
}];
//according to apple super should be called at end of method
[super updateConstraints];
}
3. mas_remakeConstraints
mas_updateConstraints對于更新一組約束很有用,但除了更新常量值之外做任何事情都會讓人筋疲力盡。 這就是mas_remakeConstraints的用武之地。
mas_remakeConstraints類似于mas_updateConstraints,但它不是更新常量值,而是在再次安裝之前刪除所有約束。 這使您可以提供不同的約束,而不必保留對要刪除的引用的引用。
- (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);
}
}];
}
您可以在Masonry iOS Examples項目中找到所有三種方法的更詳細示例。
When the ^&*!@ hits the fan!
布置您的視圖并不總是順利。 因此,你不想像這樣看控制臺輸出:
Unable to simultaneously satisfy constraints.....blah blah blah....
(
"<NSLayoutConstraint:0x7189ac0 V:[UILabel:0x7186980(>=5000)]>",
"<NSAutoresizingMaskLayoutConstraint:0x839ea20 h=--& v=--& V:[MASExampleDebuggingView:0x7186560(416)]>",
"<NSLayoutConstraint:0x7189c70 UILabel:0x7186980.bottom == MASExampleDebuggingView:0x7186560.bottom - 10>",
"<NSLayoutConstraint:0x7189560 V:|-(1)-[UILabel:0x7186980] (Names: '|':MASExampleDebuggingView:0x7186560 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7189ac0 V:[UILabel:0x7186980(>=5000)]>
Masonry為NSLayoutConstraint添加了一個類別,它覆蓋了- (NSString *)描述的默認實現(xiàn)。 現(xiàn)在,您可以為視圖和約束提供有意義的名稱,還可以輕松選擇由Masonry創(chuàng)建的約束。
這意味著你的控制臺輸出現(xiàn)在看起來像這樣:
Unable to simultaneously satisfy constraints......blah blah blah....
(
"<NSAutoresizingMaskLayoutConstraint:0x8887740 MASExampleDebuggingView:superview.height == 416>",
"<MASLayoutConstraint:ConstantConstraint UILabel:messageLabel.height >= 5000>",
"<MASLayoutConstraint:BottomConstraint UILabel:messageLabel.bottom == MASExampleDebuggingView:superview.bottom - 10>",
"<MASLayoutConstraint:ConflictingConstraint[0] UILabel:messageLabel.top == MASExampleDebuggingView:superview.top + 1>"
)
Will attempt to recover by breaking constraint
<MASLayoutConstraint:ConstantConstraint UILabel:messageLabel.height >= 5000>
有關(guān)如何設(shè)置,請查看Masonry工作區(qū)中的Masonry iOS Examples項目。
Where should I create my constraints?
@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];
}
@end
Installation - 安裝
在Podfile中
pod 'Masonry'
如果你想使用masonry而沒有那些討厭的mas_前綴。 在導(dǎo)入Masonry之前,將#define MAS_SHORTHAND添加到您的prefix.pch中
#define MAS_SHORTHAND
Code Snippets - 代碼塊
將包含的代碼片段復(fù)制到?/ Library / Developer / Xcode / UserData / CodeSnippets,以閃電般的速度編寫masonry塊!
mas_make -> [<#view#> mas_makeConstraints:^(MASConstraintMaker *make) { <#code#> }];
mas_update -> [<#view#> mas_updateConstraints:^(MASConstraintMaker *make) { <#code#> }];
mas_remake -> [<#view#> mas_remakeConstraints:^(MASConstraintMaker *make) { <#code#> }];
Features - 特點
- 不限于自動布局的子集。
NSLayoutConstraint可以做的任何事情,Masonry也可以做! - 出色的調(diào)試支持,為您的視圖和約束提供有意義的名稱
- 約束讀起來像句子。
- 沒有瘋狂的宏觀魔法。 Masonry不會用宏污染全局命名空間。
- 不基于字符串或字典,因此您可以獲得編譯時檢查。
后記
本篇主要講述了Masonry框架一些基本了解和信息,感興趣的給個贊或者關(guān)注~~~~
