Codementor Events

Masonry – AutoLayout的進化

Published Jul 30, 2017

在iOS的世界之中,如果要好好的把排版呈現出來,你必需要依靠auto layout的幫忙,auto layout定義了當物件container變大或變小時,物件的大小跟相對位置,有了auto layout的幫忙,才有辦法在4吋,5吋或之後出現的5.5吋手機上面正常呈現外觀而不會炸掉。

很不幸的是,在Objective C上面用code寫auto layout的規則,是一件很痛苦的事,請看以下的code:

[NSLayoutConstraint constraintWithItem:aView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeTop multiplier:1.0 constant:padding.top] [NSLayoutConstraint constraintWithItem:aView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeBottom multiplier:1.0 constant:padding.top] [NSLayoutConstraint constraintWithItem:aView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeLeft multiplier:1.0 constant:padding.top] [NSLayoutConstraint constraintWithItem:aView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeRight multiplier:1.0 constant:padding.top]

這幾條規則只是要講一個簡單的概念,就是將 aView 這個東西的上下左右都延展到跟它的parent一樣大。NSLayoutConstraint雖然非常靈活,但寫起來非常冗長,而且這樣的code一點都不好讀。

不過拜科技之賜(?),現代iOS世界出現了一個這樣的framework,Masonry,完全可以解決iOS autolayout過於煩瑣的問題。讓我們直接來看一下語法,上面那樣的規則,如果用Masonry寫,會怎樣呢?

[aView mas_makeConstraints:^(MASConstraintMaker *make) {
  make.edges.equalTo(superview).with.insets(UIEdgeInsetsZero);
}];

上面這段code,語意上是:我要讓aView的邊界,都跟它的superview緊貼住。跟最上面用NSLayoutConstraint寫出來的外星語一模一樣,而且用Edge來描述,相對於用很多constant=0來說,語意上更加直觀了。更重要的是,這種寫法因為語意完整的關係,所以很好記,不像visual format那樣有點像多學一個語法,如果想要在程式裡撰寫auto layout,非常推薦使用這個Lib。

Swift的對應版是 SnapKit (Link),語法基本上是一樣的。

安裝

請愛用CocoaPods(“http://cocoapods.org/"):

pod 'Masonry'

基本語法

以上面的情境為例:

[aView mas_makeConstraints:^(MASConstraintMaker *make) {
  make.edges.equalTo(superview).with.insets(UIEdgeInsetsZero);
}];

這段code是要設定讓 aView 的大小跟superview一模一樣。

為了講解方便,我把它拆成更容易理解的語法:

[aView mas_makeConstraints:^(MASConstraintMaker *make) {
  make.top.equalTo(superview.mas_top).with.offset(0);
    make.left.equalTo(superview.mas_left).with.offset(0);
    make.bottom.equalTo(superview.mas_bottom).with.offset(0);
    make.right.equalTo(superview.mas_right).with.offset(0);
}];

這段code主軸就是mas_makeConstraints這個method,這個method是說我想要開始在 aView 上面加auto layout的規則了。

在這個method裡面,主要的參數是一個block,帶有一個傳入值_make_,這個make你可以想像成就是 aView 的代表,在block裡面我們都使用_make_代替 aView 。接下來我們來看一下block裡面的第一行:

make.top.equalTo(superview.mas_top).with.offset(0);

這一行翻成白話文,指的就是我想要把 aView (make)的頂端(top)對齊(equalTo)superview的頂端(mas_top),並且設定中間沒有空隙(with.offset(0)),這裡面有幾個關鍵字:

  • top:代表actor(aView)的某個部份,可能的值有
    • top
    • bottom
    • left
    • right
    • height
    • width

…等等

  • equalTo():代表aView跟其它view之間的關係,可能的值有:^1
    • equalTo
    • lessThanOrEqualTo
    • greaterThanOrEqualTo

另外這個method裡面的參數就是要對齊的view跟該view的對齊基準點

  • mas_top:這關鍵字代表的是某個view的頂端,可能的值還有:^2_
    • mas_top_
    • mas_bottom_
    • mas_left_
    • mas_right_
    • mas_width_
    • mas_height_

…等等

  • offset():這個代表跟相對物件的關係值,以上面的code為例,就是兩個view之間的距離是0。

^1: Not all things are created equal(“https://github.com/Masonry/Masonry#not-all-things-are-created-equal")

^2: MASViewAttribute(“https://github.com/Masonry/Masonry#1-masviewattribute")

更多範例

上面示範了view對齊其它view的做法,那如果我想要指定特定view的寬度呢?

[aView mas_makeConstraints:^(MASConstraintMaker *make) { make.width.equalTo(@400);
}];

你可以直接這樣下,代表的就是我想要讓aView的寬度固定在400。要注意的是,equalTo如果是傳某個特定的值,那它一定要是NSNumber不能是primitive value。

設定優先權

如果你想要設定規則的優先權,可以在with這個關鍵字之後,下.priority(),如下:

[aView mas_makeConstraints:^(MASConstraintMaker *make) { make.width.equalTo(@400).with.priority(1000);
}];

或者使用其它可能的關鍵字:^3

  • .prority()
  • .priorityHigh
  • .priorityMedium
  • .priorityLow

後面三個都是常數值,直接定義好讓你方便取用。

看完了這些簡單的應用,有沒有覺得生命獲得救贖阿?

更多資訊可以上他們的Github(“https://github.com/Masonry/Masonry")上看,我上面這些介紹只是初階中的初階而已。

歡迎討論。

^3: Learn to prioritize(“https://github.com/Masonry/Masonry#learn-to-prioritize")

Discover and read more posts from ShihTing Huang (Neo)
get started