Masonry使用詳解

Masonry -- 使用純代碼進行iOS應用的autolayout自適應布局,對很多零散的進行整理

?簡介

簡化iOS應用使用純代碼機型自適應布局的工作,使用一種簡潔高效的語法替代NSLayoutConstraints.

項目主頁:?Masonry

最新示例:?點擊下載

項目簡議: 如果再看到關于純代碼,xib或storyboard,使用哪種方式進行UI布局更合適的討論,請推薦他們先試用下 Masonry. Masonry,像xib一樣快速,同時擁有作為純代碼方式的靈活性 -- github關注度 7800 + 是有原因的!

快速入門

安裝

使用 CocoaPods 安裝

```

pod 'Masonry'

```

推薦在你的在 prefix.pch 中引入頭文件:

```

// 定義這個常量,就可以在使用Masonry不必總帶著前綴 `mas_`:

#define MAS_SHORTHAND

// 定義這個常量,以支持在 Masonry 語法中自動將基本類型轉換為 object 類型:

#define MAS_SHORTHAND_GLOBALS

#import "Masonry.h"

```

使用

初始Masonry

這是使用MASConstraintMaker創建的約束:

```

/* 注意:view1應首先添加為某個視圖的子視圖,superview是一個局部變量,指view1的父視圖. */

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {

make.top.equalTo(superview.mas_top).offset(padding.top);

make.left.equalTo(superview.mas_left).offset(padding.left);

make.bottom.equalTo(superview.mas_bottom).offset(-padding.bottom);

make.right.equalTo(superview.mas_right).offset(-padding.right);

}];

```

甚至可以更短:

```

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {

make.edges.equalTo(superview).insets(padding);

}];

```

## 不止可以表達相等關系

> `.equalTo` 等價于 NSLayoutRelationEqual

> `.lessThanOrEqualTo` 等價于 NSLayoutRelationLessThanOrEqual

> `.greaterThanOrEqualTo` 等價于 NSLayoutRelationGreaterThanOrEqual

這三個表達相等關系的語句,可以接受一個參數;此參數可以為以下任意一個:

1、MASViewAttribute

```

make.centerX.lessThanOrEqualTo(view2.mas_left);

```

| 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

如果你需要 view.left 大于或等于label.left:

```

// 下面兩個約束是完全等效的.

make.left.greaterThanOrEqualTo(label);

make.left.greaterThanOrEqualTo(label.mas_left);

```

3、NSNumber

自適應布局允許將寬度或高度設置為固定值.如果你想要給視圖一個最小或最大值,你可以這樣:

```

//width >= 200 && width <= 400

make.width.greaterThanOrEqualTo(@200);

make.width.lessThanOrEqualTo(@400)

```

但是自適應布局不支持將 left,right, centerY等設為固定值.如果你給這些屬性傳遞一個常量, Masonry會自動將它們轉換為相對于其父視圖的相對值:

```

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

```

4、NSArry

一個數組,里面可以混合是前述三種類型的任意幾種:

```

// 表達三個視圖等高的約束.

make.height.equalTo(@[view1.mas_height, view2.mas_height]);

make.height.equalTo(@[view1, view2]);

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

```

#### 兩個特殊的等比方法

* 第一種

![](http://upload-images.jianshu.io/upload_images/2162015-da44bf4b329efc83.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

```

//在紅色View里面放三個正方形View, 等間距為10

NSInteger padding = 10;

UIView *yellowView1 = [[UIView alloc] init];

yellowView1.backgroundColor = [UIColor yellowColor];

[redView addSubview:yellowView1];

UIView *yellowView2 = [[UIView alloc] init];

yellowView2.backgroundColor = [UIColor yellowColor];

[redView addSubview:yellowView2];

UIView *yellowView3 = [[UIView alloc] init];

yellowView3.backgroundColor = [UIColor yellowColor];

[redView addSubview:yellowView3];

[@[yellowView1, yellowView2, yellowView3] mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:padding leadSpacing:padding tailSpacing:padding];

[@[yellowView1, yellowView2, yellowView3] mas_makeConstraints:^(MASConstraintMaker *make) {

make.top.equalTo(redView).offset(10);

make.height.mas_equalTo(yellowView3.mas_width);

}];

```

```

/**

*? 確定間距等間距布局

*

*? @param axisType? ? 布局方向

*? @param fixedSpacing 兩個item之間的間距(最左面的item和左邊, 最右邊item和右邊都不是這個)

*? @param leadSpacing? 第一個item到父視圖邊距

*? @param tailSpacing? 最后一個item到父視圖邊距

*/

- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;

```

所以也就知道了, 將`fixedSpacing`, `leadSpacing`, `tailSpacing`都賦值同一個間距, 數組內的的View就會自動計算出寬度, 完成水平方向的布局.

要注意的是, 這個方法僅僅完成了水平方向的布局, 如果想確定這幾個View的位置, 還需要指定豎直方向位置和高度, 這里可以用數組直接調用 `mas_makeConstraints:^(MASConstraintMaker *make){}`完成布局.

* 第二種

![](http://upload-images.jianshu.io/upload_images/2162015-f1e9484bda5f5612.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

```

/**

*? distribute with fixed item size

*

*? @param axisType? 布局方向

*? @param fixedItemLength 每個item的布局方向的長度

*? @param leadSpacing? 第一個item到父視圖邊距

*? @param tailSpacing? 最后一個item到父視圖邊距

*/

- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;

```

區別就是這里除了布局方向, 第一個和最后一個View的邊距, 這里需要指定的是每個item的長度, 自動計算間隙, 所以這個要實現等間距, 其實是要通過item的數量, 以及父視圖的寬度先計算出間距, 然后賦值給, leadSpacing和tailSpacing, 比如`CGFloat padding2 = (300 - 3 * 30) / 4;` 這里的300就是父視圖的寬度, 30是指定的每個item的寬度, 這樣計算好就可以保證, leadSpacing, tailSpacing, 和item之間的間距相同, 實現布局.

同樣這個方法完成了水平方向的布局, 還需要完成豎直方向的布局.

* 第三種

![](http://upload-images.jianshu.io/upload_images/2162015-4cc19492281e48fe.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

```

//在紅色View里面放三個大小不一樣的綠色正方形, 間隙等大, masonry并沒提供相關方法

NSMutableArray *greenViews = [NSMutableArray array];

for (NSInteger i = 0; i < 3; i++) {

UIView *greenView = [[UIView alloc] init];

greenView.backgroundColor = [UIColor greenColor];

[redView addSubview:greenView];

[greenViews addObject:greenView];

[greenView mas_makeConstraints:^(MASConstraintMaker *make) {

make.bottom.equalTo(redView).offset(-10);

make.width.mas_equalTo(i*20 + 20);

make.height.mas_equalTo(greenView.mas_width);

}];

}

[redView distributeSpacingHorizontallyWith:greenViews];

```

首先在for循環內 , 完成了底部位置, 寬, 高的布局, 還缺少水平方向的位置, 即還要確定每個view的X, 這里用到了一個UIView的分類

`- (void) distributeSpacingHorizontallyWith:(NSArray*)views;`

這個分類直接用的里脊串的一篇文章中的代碼, 就不貼出代碼了, 簡單說一下原理, 如圖所示:

![](http://upload-images.jianshu.io/upload_images/2162015-7611cd1ea333175e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

實現原理就是在View中創建greenViews.count + 1個占位的View(藍色), 之后通過布局, 使占位View與要布局的View依次排開, 左右間距為0, 同時要約束所有的占位View寬度相等, 這樣看來, 這些占位View的寬度, 就是greenViews的間距, 也就可以實現等間距布局了.

## 約束的優先級

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

```

## 等比例自適應

> `.multipliedBy` 允許你指定一個兩個視圖的某個屬性等比例變化

>

> `item1.attribute1 = multiplier × item2.attribute2 + constant`,此為約束的計算公式, `.multipliedBy`本質上是用來限定 `multiplier`的

> 注意,因為編程中的坐標系從父視圖左上頂點開始,所以指定基于父視圖的left或者top的multiplier是沒有意義的,因為父視圖的left和top總為0.

> 如果你需要一個視圖隨著父視圖的寬度和高度,位置自動變化,你應該同時指定 right,bottom,width,height與父視圖對應屬性的比例(基于某個尺寸下的相對位置計算出的比例),并且constant必須為0.

```

// 指定寬度為父視圖的 1/4.

make.width.equalTo(superview).multipliedBy(0.25);

```

## 工具方法

Masonry提供了一些工具方法來進一步簡化約束的創建.

### edges 邊界

```

//使 top, left, bottom, right等于 view2

make.edges.equalTo(view2);

//使 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 尺寸

```

// 使寬度和高度大于或等于 titleLabel

make.size.greaterThanOrEqualTo(titleLabel)

//使 width = superview.width + 100, height = superview.height - 50

make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50))

```

### center 中心

```

//使 centerX和 centerY = button1

make.center.equalTo(button1)

//使 centerX = superview.centerX - 5, centerY = superview.centerY + 10

make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10))

```

你可以使用鏈式語法來增強代碼可讀性:

```

// 除top外,其他約束都與父視圖相等.

make.left.right.bottom.equalTo(superview);

make.top.equalTo(otherView);

```

## 更新約束

有時,你需要修改已經存在的約束來實現動畫效果或者移除/替換已有約束.在 Masonry 中,有幾種不同的更新視圖約束的途徑:

#### 1、References 引用

你可以把 Masonry 語法返回的約束或約束數組,存儲到一個局部變量或者類的屬性中,以供后續操作某個約束.

```

// 聲明屬性

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

}];

...

// 然后你就可以操作這個屬性.

[self.topConstraint uninstall];

```

#### 2、mas_updateConstraints

如果你只是想添加新的約束,你可以使用便利方法mas_updateConstraints,不需要使用 mas_makeConstraints. mas_updateConstraints,不會移除已經存在的約束(即使新舊約束間相互沖突).

```

// 重寫視圖的updateConstraints方法: 這是Apple推薦的添加/更新約束的位置.

// 這個方法可以被多次調用以響應setNeedsUpdateConstraints方法.

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

}];

//根據apple機制,最后應調用父類的updateConstraints方法.

[super updateConstraints];

}

```

#### 3. mas_remakeConstraints

`mas_remakeConstraints`與`mas_updateConstraints`相似,不同之處在于: `mas_remakeConstraints` 會先移除視圖上已有的約束,再去創建新的約束.

```

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

}

}];

}

```

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容

  • 一、前言 關于蘋果的布局一直是我比較糾結的問題,是寫代碼來控制布局,還是使用storyboard來控制布局呢?以前...
    iplaycodex閱讀 2,466評論 0 1
  • Masonry是一個輕量級的布局框架,擁有自己的描述語法,采用更優雅的鏈式語法封裝自動布局,簡潔明了并具有高可讀性...
    3dcc6cf93bb5閱讀 1,782評論 0 1
  • (一)Masonry介紹 Masonry是一個輕量級的布局框架 擁有自己的描述語法 采用更優雅的鏈式語法封裝自動布...
    木易林1閱讀 2,364評論 0 3
  • iOS_autoLayout_Masonry 概述 Masonry是一個輕量級的布局框架與更好的包裝AutoLay...
    指尖的跳動閱讀 1,178評論 1 4
  • Pink Floyd的《迷墻》燃起我對搖滾樂的興趣,然而我在嘗試欣賞Pink的其他作品時卻再難感受到,來自心的觸動...
    社長墨非閱讀 230評論 0 0