Masonry(Github)文檔翻譯

Masonry以更漂亮的語(yǔ)法封裝了AutoLayout,是一個(gè)輕量級(jí)的布局框架。
Masonry有自己的布局DSL(domain-specific language),提供了一種鏈?zhǔn)綄懛▉?lái)描述NSLayoutConstraints,這使得布局代碼變得更加簡(jiǎn)潔和可讀。
Masonry 支持 iOS 和 macOS。


NSLayoutConstraints怎么了?

在底層AutoLayout是一種很強(qiáng)大靈活的用于管理和布局視圖的方式。但用代碼創(chuàng)建約束(Constraints)顯得非常冗長(zhǎng)且不易描述。設(shè)想這樣的簡(jiǎn)單場(chǎng)景:在父視圖中填放一個(gè)子視圖,且每一邊留下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],

 ]];

即使這樣簡(jiǎn)單的一個(gè)例子,代碼就變得這么冗長(zhǎng),如果再多兩三個(gè)視圖,就立刻變得可讀性很差。
另外一個(gè)選擇是使用VFL,稍微不那么長(zhǎng)和繞。但是ASKII類型的語(yǔ)法有它自己的隱患,并且動(dòng)畫(huà)很麻煩因?yàn)?em>NSLayoutConstraint constraintsWithVisualFormat:返回的是一個(gè)數(shù)組。


準(zhǔn)備好見(jiàn)你的Maker!

以下是用MASConstraintMaker創(chuàng)建的簡(jiǎn)單約束:

    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);
}];

不過(guò)需要注意在第一個(gè)例子中我們需要把約束添加到superview中,盡管Masonry會(huì)自動(dòng)添加約束到合適的view中。
Masonry也會(huì)自動(dòng)調(diào)用

view1.translatesAutoresizidngMaskIntoConstraints = NO;

不是所有的事情都是平等的

.equalTo 等價(jià)于 NSLayoutRelationEqual
.lessThanOrEqualTo 等價(jià)于 NSLayoutRelationLessThanOrEqual
.greaterThanOrEqualTo 等價(jià)于 NSLayoutRelationGreaterThanOrEqual

這3個(gè)等價(jià)關(guān)系接受一個(gè)參數(shù),可以是下面的任何一種:

1、MASViewAttribute

make.centerX.lessThanOrEqualTo(view2.mas_left);

以下是MASViewAttribute和NSLayoutAttribute 的對(duì)應(yīng)關(guān)系:

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

2. UIView/NSView

if you want view.left to be greater than or equal to label.left :
//這2中約束是等價(jià)的
make.left.greaterThanOrEqualTo(label);
make.left.greaterThanOrEqualTo(label.mas_left);

3. NSNumber

Auto Layout允許寬和高設(shè)置為常量。如果想設(shè)置view的最小和最大寬度,可以傳一個(gè)number值:

//width >= 200 && width <= 400
make.width.greaterThanOrEqualTo(@200);
make.width.lessThanOrEqualTo(@400);

然而Auto Layout 不允許left, right, centerY等的對(duì)齊屬性設(shè)置成常量值。所以,給這些屬性傳NSNumber,Masonry會(huì)轉(zhuǎn)換到view的superview中去,比如:

//creates view.left = view.superview.left + 10
make.left.lessThanOrEqualTo(@10)

除了NSNumber,也可以用原始值或結(jié)構(gòu)體來(lái)建立約束,如:

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));

一般支持自動(dòng)裝箱(autoboxing)的宏會(huì)有mas_前綴。沒(méi)前綴的版本在導(dǎo)入Masronry前,通過(guò)定義MAS_SHORTHAND_GLOBALS,也是可以用的。

4. NSArray

包含前面任何類型元素的數(shù)組也是可以的。

make.height.equalTo(@[view1.mas_height, view2.mas_height]);
make.height.equalTo(@[view1, view2]);
make.left.equalTo(@[view1, @100, view3.right]);

考慮優(yōu)先級(jí)(prioritize)

.priority 允許你指定一個(gè)確定的優(yōu)先級(jí)
.priorityHigh 等價(jià)于 UILayoutPriorityDefaultHigh
.priorityMedium 介于高優(yōu)先級(jí)和低優(yōu)先級(jí)之間
.priorityLow 等價(jià)于UILayoutPriorityDefaultLow

優(yōu)先級(jí)可以添加在約束鏈后面,像這樣:

make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow();
make.top.equalTo(label.mas_top).with.priority(600);

結(jié)構(gòu),結(jié)構(gòu),結(jié)構(gòu)

Masonry也提供了一些簡(jiǎn)便方法來(lái)同時(shí)創(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))

為了可讀性高也可以把屬性連起來(lái)像這樣:

// All edges but the top should equal those of the superview

make.left.right.and.bottom.equalTo(superview);
make.top.equalTo(otherView);

讓美好持續(xù)發(fā)生

有時(shí)你需要修改已存在的約束來(lái)支持動(dòng)畫(huà)或者移除、替換約束。在Masonry中有一些不同的方式來(lái)更新約束。

1、引用

可以通過(guò)創(chuàng)建變量或者屬性來(lái)引用一個(gè)特定的約束。也可以通過(guò)數(shù)組保存的方式來(lái)引用多個(gè)約束。

// 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);
}];

...

// 然后你可以調(diào)用

 [self.topConstraint uninstall];

2. mas_updateConstraints

如果只是更新約束中的常量值,你可以用這個(gè)mas_updateConstraints方法來(lái)代替mas_makeConstraints
// 這是蘋果推薦的用來(lái)添加或更新約束的地方
// 這個(gè)方法會(huì)在響應(yīng)setNeedsUpdateConstraints時(shí)被多次調(diào)用
// 會(huì)被UIKit內(nèi)部或者在你的代碼中當(dāng)你需要觸發(fā)約束的更新時(shí)調(diào)用

- (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在更新一組約束時(shí)很有用,但是做一些更新約束值之外的事就顯得力不從心了。這就是為何要引入mas_remakeConstraints。

mas_remakeConstraints類似mas_updateConstraints,但是不同于更新常量值,它會(huì)在重新安裝約束前移除所有的約束。這就讓你能提供不同的約束,而不用時(shí)刻記住你要移除哪個(gè)約束了。

- (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);
        }
    }];
}

出現(xiàn)麻煩咋整!

布局并不能像你意料的那樣,所以當(dāng)事情真的搞砸了的時(shí)候,你肯定不希望在控制臺(tái)看到這樣的輸出:

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增加了一個(gè)分類,覆蓋了*- (NSString )description的默認(rèn)實(shí)現(xiàn)。這樣在給約束和view起名字后,能夠很輕松的從中找出哪些是Masonry創(chuàng)建的約束。

這意味著你的控制臺(tái)輸出看起來(lái)是這樣的:

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>

應(yīng)該在哪創(chuàng)建約束?

@implementation DIYCustomView

- (id)init {
    self = [super init];
    if (!self) return nil;

    // --- Create your views here ---
    self.button = [[UIButton alloc] init];

    return self;
}

// 告訴UIKit你在使用AutoLayout
+ (BOOL)requiresConstraintBasedLayout {
    return YES;
}

// 這是蘋果推薦用來(lái)添加或更新約束的地方
- (void)updateConstraints {

    // 在這里更新或者添加約束
    [self.button remakeConstraints:^(MASConstraintMaker *make) {
        make.width.equalTo(@(self.buttonSize.width));
        make.height.equalTo(@(self.buttonSize.height));
    }];
    
    //根據(jù)蘋果介紹super方法應(yīng)該在方法的最后調(diào)用一下
    [super updateConstraints];
}

- (void)didTapButton:(UIButton *)button {
    // --- Do your changes ie change variables that affect your layout etc ---
    self.buttonSize = CGSize(200, 200);

    // 通知約束他們應(yīng)該被更新了
    [self setNeedsUpdateConstraints];
}

@end

安裝:

使用CocoaPods.
在Podfile文件中寫:

pod ‘Masonry'  

即可。

如果你在用Masonry時(shí)不想見(jiàn)到討厭的mas_前綴,就添加

#define MAS_SHORTHAND

到prefix.pch,并導(dǎo)入到Masonry中。


便捷代碼塊

將以下代碼塊拷貝到~/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>}];

特性:

1、不只是Auto Layout的子集,任何NSLayoutConstraint能做的,Masonry都能做
2、很棒的debug支持,只要你給約束和view取好有意義的名字
3、約束讀起來(lái)像自然語(yǔ)句,通俗易懂
4、沒(méi)有令人抓狂的宏魔法。Masonry有了宏不會(huì)污染全局的名字空間。
5、不基于字符串和字典,所以編譯的時(shí)候就能檢查出問(wèn)題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Autolayout就像一個(gè)知情達(dá)理,善解人意的好姑娘,可惜長(zhǎng)相有點(diǎn)不堪入目,所以追求者寥寥無(wú)幾。所幸遇到了化妝大...
    小笨狼閱讀 24,202評(píng)論 28 227
  • 本文內(nèi)容全部轉(zhuǎn)載自追求Masonry 目錄 『使用』 一、MASConstraintMaker二、MASConst...
    Vinc閱讀 3,601評(píng)論 4 18
  • 一、前言 關(guān)于蘋果的布局一直是我比較糾結(jié)的問(wèn)題,是寫代碼來(lái)控制布局,還是使用storyboard來(lái)控制布局呢?以前...
    iplaycodex閱讀 2,729評(píng)論 0 1
  • 《善良沒(méi)用,你得有錢》 ---- 文/四營(yíng) 上面原文 1 善良沒(méi)有,你得有錢。 有錢不善良,你能看嗎?能引起感動(dòng)嗎...
    孤獨(dú)的郭先生閱讀 831評(píng)論 0 0
  • 我還記得去年分手的時(shí)候,他的手輕輕在我頭上揉了揉,無(wú)奈而又溫柔的的嗓音輕輕的對(duì)我說(shuō):你終究要慢慢長(zhǎng)大啊。 那是我最...
    3dafd90caf21閱讀 680評(píng)論 0 0

友情鏈接更多精彩內(nèi)容