Masonry介紹與使用

本文Demo連接 https://github.com/developerSunquan/Masonry-Isometricios

前言git

1
MagicNumber -> autoresizingMask -> autolayout

以上是純手寫代碼所經歷的關於頁面佈局的三個時期github

在iphone1-iphone3gs時代 window的size固定爲(320,480) 咱們只須要簡單計算一下相對位置就行了框架

在iphone4-iphone4s時代 蘋果推出了retina屏 可是給了碼農們很是大的福利:window的size不變less

在iphone5-iphone5s時代 window的size變了(320,568) 這時autoresizingMask派上了用場(爲啥這時候不用Autolayout? 由於還要支持ios5唄) 簡單的適配一下便可dom

在iphone6+時代 window的width也發生了變化(相對5和5s的屏幕比例沒有變化) 終因而時候拋棄autoresizingMask改用autolayout了(不用支持ios5了 相對於屏幕適配的多樣性來講autoresizingMask也已通過時了)iphone

那如何快速的上手autolayout呢? 說實話 當年ios6推出的同時新增了autolayout的特性 我看了一下官方文檔和demo 就立馬拋棄到一邊了 由於實在過於的繁瑣和囉嗦(有過經驗的朋友確定有同感)ide

直到iPhone6發佈以後 我知道使用autolayout勢在必行了 這時想起了之前在瀏覽Github看到過的一個第三方庫Masonry 在花了幾個小時的研究使用後 我就將autolayout掌握了(重點是我並無學習任何的官方文檔或者其餘的關於autolayout的知識) 這就是我爲何要寫下這篇文章來推薦它的緣由.函數

介紹佈局

Masonry 源碼:https://github.com/Masonry/Masonry

Masonry是一個輕量級的佈局框架 擁有本身的描述語法 採用更優雅的鏈式語法封裝自動佈局 簡潔明瞭 並具備高可讀性 並且同時支持 iOS 和 Max OS X。

咱們先來看一段官方的sample code來認識一下Masonry

1
2
3
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
     make.edges.equalTo(superview). with .insets(padding);
}];

看到block裏面的那句話: make edges equalTo superview with insets

經過鏈式的天然語言 就把view1給autolayout好了 是否是簡單易懂?

使用

看一下Masonry支持哪一些屬性

1
2
3
4
5
6
7
8
9
10
11
@property (nonatomic, strong, readonly) MASConstraint *left;
@property (nonatomic, strong, readonly) MASConstraint *top;
@property (nonatomic, strong, readonly) MASConstraint *right;
@property (nonatomic, strong, readonly) MASConstraint *bottom;
@property (nonatomic, strong, readonly) MASConstraint *leading;
@property (nonatomic, strong, readonly) MASConstraint *trailing;
@property (nonatomic, strong, readonly) MASConstraint *width;
@property (nonatomic, strong, readonly) MASConstraint *height;
@property (nonatomic, strong, readonly) MASConstraint *centerX;
@property (nonatomic, strong, readonly) MASConstraint *centerY;
@property (nonatomic, strong, readonly) MASConstraint *baseline;

這些屬性與NSLayoutAttrubute的對照表以下

43.jpg

其中leading與left trailing與right 在正常狀況下是等價的 可是當一些佈局是從右至左時(好比阿拉伯文?沒有相似的經驗) 則會對調 換句話說就是基本能夠不理不用 用left和right就行了

在ios8發佈後 又新增了一堆奇奇怪怪的屬性(有興趣的朋友能夠去瞅瞅) Masonry暫時還不支持(不過你要支持ios6,ios7 就不必去管那麼多了)

在講實例以前 先介紹一個MACRO

1
#define WS(weakSelf)  __weak __typeof(&*self)weakSelf = self;

快速的定義一個weakSelf 固然是用於block裏面啦 下面進入正題(爲了方便 咱們測試的superView都是一個size爲(300,300)的UIView)

下面 經過一些簡單的實例來簡單介紹如何輕鬆愉快的使用Masonry:

1. [基礎] 居中顯示一個view

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (void)viewDidLoad
{
     [ super  viewDidLoad];
     // Do any additional setup after loading the view.
     
     WS(ws);
     
     UIView *sv = [UIView  new ];
     [sv showPlaceHolder];
     sv.backgroundColor = [UIColor blackColor];
     [self.view addSubview:sv];
     [sv mas_makeConstraints:^(MASConstraintMaker *make) {
         make.center.equalTo(ws.view);
         make.size.mas_equalTo(CGSizeMake(300, 300));
     }];
     
}

代碼效果

04.PNG

 

使用我之間寫的MMPlaceHolder 能夠看到superview已經按照咱們預期居中而且設置成了適當的大小

那麼先看看這幾行代碼

1
2
3
4
5
6
7
8
9
10
11
12
//今後之後基本能夠拋棄CGRectMake了
UIView *sv = [UIView  new ];
//在作autoLayout以前 必定要先將view添加到superview上 不然會報錯
[self.view addSubview:sv];
//mas_makeConstraints就是Masonry的autolayout添加函數 將所需的約束添加到block中行了
[sv mas_makeConstraints:^(MASConstraintMaker *make) {
//將sv居中(很容易理解吧?)
     make.center.equalTo(ws.view);
     
     //將size設置成(300,300)
     make.size.mas_equalTo(CGSizeMake(300, 300));
}];

這裏有兩個問題要分解一下

首先在Masonry中可以添加autolayout約束有三個函數

1
2
3
4
5
6
7
8
9
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block;
- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block;
- (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block;
/*
mas_makeConstraints 只負責新增約束 Autolayout不能同時存在兩條針對於同一對象的約束 不然會報錯 
mas_updateConstraints 針對上面的狀況 會更新在block中出現的約束 不會致使出現兩個相同約束的狀況
mas_remakeConstraints 則會清除以前的全部約束 僅保留最新的約束
三種函數善加利用 就能夠應對各類狀況了
*/

其次 equalTo 和 mas_equalTo的區別在哪裏呢? 其實 mas_equalTo是一個MACRO

1
2
3
4
#define mas_equalTo(...)                 equalTo(MASBoxValue((__VA_ARGS__)))
#define mas_greaterThanOrEqualTo(...)    greaterThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_lessThanOrEqualTo(...)       lessThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_offset(...)                  valueOffset(MASBoxValue((__VA_ARGS__)))

能夠看到 mas_equalTo只是對其參數進行了一個BOX操做(裝箱) MASBoxValue的定義具體能夠看看源代碼 太長就不貼出來了

所支持的類型 除了NSNumber支持的那些數值類型以外 就只支持CGPoint CGSize UIEdgeInsets

介紹完這幾個問題 咱們就繼續往下了 PS:剛纔定義的sv會成爲咱們接下來全部sample的superView

2. [初級] 讓一個view略小於其superView(邊距爲10)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
UIView *sv1 = [UIView  new ];
[sv1 showPlaceHolder];
sv1.backgroundColor = [UIColor redColor];
[sv addSubview:sv1];
[sv1 mas_makeConstraints:^(MASConstraintMaker *make) {
     make.edges.equalTo(sv). with .insets(UIEdgeInsetsMake(10, 10, 10, 10));
     
     /* 等價於
     make.top.equalTo(sv).with.offset(10);
     make.left.equalTo(sv).with.offset(10);
     make.bottom.equalTo(sv).with.offset(-10);
     make.right.equalTo(sv).with.offset(-10);
     */
     
     /* 也等價於
     make.top.left.bottom.and.right.equalTo(sv).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));
     */
}];

代碼效果

05.PNG

能夠看到 edges 其實就是top,left,bottom,right的一個簡化 分開寫也能夠 一句話更省事

那麼爲何bottom和right裏的offset是負數呢? 由於這裏計算的是絕對的數值 計算的bottom須要小魚sv的底部高度 因此要-10 同理用於right

這裏有意思的地方是and和with 其實這兩個函數什麼事情都沒作

1
2
3
4
5
6
- (MASConstraint *) with  {
     return  self;
}
- (MASConstraint *)and {
     return  self;
}

可是用在這種鏈式語法中 就很是的巧妙和易懂 不得不佩服做者的心思(雖然我如今基本都會省略)

3. [初級] 讓兩個高度爲150的view垂直居中且等寬且等間隔排列 間隔爲10(自動計算其寬度)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int padding1 = 10;
[sv2 mas_makeConstraints:^(MASConstraintMaker *make) {
     make.centerY.mas_equalTo(sv.mas_centerY);
     make.left.equalTo(sv.mas_left). with .offset(padding1);
     make.right.equalTo(sv3.mas_left). with .offset(-padding1);
     make.height.mas_equalTo(@150);
     make.width.equalTo(sv3);
}];
[sv3 mas_makeConstraints:^(MASConstraintMaker *make) {
     make.centerY.mas_equalTo(sv.mas_centerY);
     make.left.equalTo(sv2.mas_right). with .offset(padding1);
     make.right.equalTo(sv.mas_right). with .offset(-padding1);
     make.height.mas_equalTo(@150);
     make.width.equalTo(sv2);
}];

代碼效果

06.PNG

這裏咱們在兩個子view之間互相設置的約束 能夠看到他們的寬度在約束下自動的被計算出來了

4. [中級] 在UIScrollView順序排列一些view並自動計算contentSize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
UIScrollView *scrollView = [UIScrollView  new ];
scrollView.backgroundColor = [UIColor whiteColor];
[sv addSubview:scrollView];
[scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.edges.equalTo(sv). with .insets(UIEdgeInsetsMake(5,5,5,5));
}];
UIView *container = [UIView  new ];
[scrollView addSubview:container];
[container mas_makeConstraints:^(MASConstraintMaker *make) {
     make.edges.equalTo(scrollView);
     make.width.equalTo(scrollView);
}];
int count = 10;
UIView *lastView = nil;
for  ( int i = 1 ; i <= count ; ++i )
{
     UIView *subv = [UIView  new ];
     [container addSubview:subv];
     subv.backgroundColor = [UIColor colorWithHue:( arc4random() % 256 / 256.0 )
                                       saturation:( arc4random() % 128 / 256.0 ) + 0.5
                                       brightness:( arc4random() % 128 / 256.0 ) + 0.5
                                            alpha:1];
     
     [subv mas_makeConstraints:^(MASConstraintMaker *make) {
         make.left.and.right.equalTo(container);
         make.height.mas_equalTo(@(20*i));
         
         if  ( lastView )
         {
             make.top.mas_equalTo(lastView.mas_bottom);
         }
         else
         {
             make.top.mas_equalTo(container.mas_top);
         }
     }];
     
     lastView = subv;
}
[container mas_makeConstraints:^(MASConstraintMaker *make) {
     make.bottom.equalTo(lastView.mas_bottom);
}];

頭部效果

07.PNG

 

尾部效果

08.PNG

從scrollView的scrollIndicator能夠看出 scrollView的內部已如咱們所想排列好了

這裏的關鍵就在於container這個view起到了一箇中間層的做用 可以自動的計算uiscrollView的contentSize

5. [高級] 橫向或者縱向等間隙的排列一組view

很遺憾 autoLayout並無直接提供等間隙排列的方法(Masonry的官方demo中也沒有對應的案例) 可是參考案例3 咱們能夠經過一個小技巧來實現這個目的 爲此我寫了一個Category

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
@implementation UIView(Masonry_LJC)
- (void) distributeSpacingHorizontallyWith:(NSArray*)views
{
     NSMutableArray *spaces = [NSMutableArray arrayWithCapacity:views.count+1];
     
     for  ( int i = 0 ; i < views.count+1 ; ++i )
     {
         UIView *v = [UIView  new ];
         [spaces addObject:v];
         [self addSubview:v];
         
         [v mas_makeConstraints:^(MASConstraintMaker *make) {
             make.width.equalTo(v.mas_height);
         }];
     }    
     
     UIView *v0 = spaces[0];
     
     __weak __typeof(&*self)ws = self;
     
     [v0 mas_makeConstraints:^(MASConstraintMaker *make) {
         make.left.equalTo(ws.mas_left);
         make.centerY.equalTo(((UIView*)views[0]).mas_centerY);
     }];
     
     UIView *lastSpace = v0;
     for  ( int i = 0 ; i < views.count; ++i )
     {
         UIView *obj = views[i];
         UIView *space = spaces[i+1];
         
         [obj mas_makeConstraints:^(MASConstraintMaker *make) {
             make.left.equalTo(lastSpace.mas_right);
         }];
         
         [space mas_makeConstraints:^(MASConstraintMaker *make) {
             make.left.equalTo(obj.mas_right);
             make.centerY.equalTo(obj.mas_centerY);
             make.width.equalTo(v0);
         }];
         
         lastSpace = space;
     }
     
     [lastSpace mas_makeConstraints:^(MASConstraintMaker *make) {
         make.right.equalTo(ws.mas_right);
     }];
     
}
- (void) distributeSpacingVerticallyWith:(NSArray*)views
{
     NSMutableArray *spaces = [NSMutableArray arrayWithCapacity:views.count+1];
     
     for  ( int i = 0 ; i < views.count+1 ; ++i )
     {
         UIView *v = [UIView  new ];
         [spaces addObject:v];
         [self addSubview:v];
         
         [v mas_makeConstraints:^(MASConstraintMaker *make) {
             make.width.equalTo(v.mas_height);
         }];
     }
     
     
     UIView *v0 = spaces[0];
     
     __weak __typeof(&*self)ws = self;
     
     [v0 mas_makeConstraints:^(MASConstraintMaker *make) {
         make.top.equalTo(ws.mas_top);
         make.centerX.equalTo(((UIView*)views[0]).mas_centerX);
     }];
     
     UIView *lastSpace = v0;
     for  ( int i = 0 ; i < views.count; ++i )
     {
         UIView *obj = views[i];
         UIView *space = spaces[i+1];
         
         [obj mas_makeConstraints:^(MASConstraintMaker *make) {
             make.top.equalTo(lastSpace.mas_bottom);
         }];
         
         [space mas_makeConstraints:^(MASConstraintMaker *make) {
             make.top.equalTo(obj.mas_bottom);
             make.centerX.equalTo(obj.mas_centerX);
             make.height.equalTo(v0);
         }];
         
         lastSpace = space;
     }
     
     [lastSpace mas_makeConstraints:^(MASConstraintMaker *make) {
         make.bottom.equalTo(ws.mas_bottom);
     }];
}
@end

簡單的來測試一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
UIView *sv11 = [UIView  new ];
UIView *sv12 = [UIView  new ];
UIView *sv13 = [UIView  new ];
UIView *sv21 = [UIView  new ];
UIView *sv31 = [UIView  new ];
sv11.backgroundColor = [UIColor redColor];
sv12.backgroundColor = [UIColor redColor];
sv13.backgroundColor = [UIColor redColor];
sv21.backgroundColor = [UIColor redColor];
sv31.backgroundColor = [UIColor redColor];
[sv addSubview:sv11];
[sv addSubview:sv12];
[sv addSubview:sv13];
[sv addSubview:sv21];
[sv addSubview:sv31];
//給予不一樣的大小 測試效果
[sv11 mas_makeConstraints:^(MASConstraintMaker *make) {
     make.centerY.equalTo(@[sv12,sv13]);
     make.centerX.equalTo(@[sv21,sv31]);
     make.size.mas_equalTo(CGSizeMake(40, 40));
}];
[sv12 mas_makeConstraints:^(MASConstraintMaker *make) {
     make.size.mas_equalTo(CGSizeMake(70, 20));
}];
[sv13 mas_makeConstraints:^(MASConstraintMaker *make) {
     make.size.mas_equalTo(CGSizeMake(50, 50));
}];
[sv21 mas_makeConstraints:^(MASConstraintMaker *make) {
     make.size.mas_equalTo(CGSizeMake(50, 20));
}];
[sv31 mas_makeConstraints:^(MASConstraintMaker *make) {
     make.size.mas_equalTo(CGSizeMake(40, 60));
}];
[sv distributeSpacingHorizontallyWith:@[sv11,sv12,sv13]];
[sv distributeSpacingVerticallyWith:@[sv11,sv21,sv31]];
[sv showPlaceHolderWithAllSubviews];
[sv hidePlaceHolder];

代碼效果

09.PNG

perfect! 簡潔明瞭的達到了咱們所要的效果

這裏所用的技巧就是 使用空白的佔位view來填充咱們目標view的旁邊 這點經過圖上的空白標註能夠看出來

小結

經過以上5個案例 我以爲已經把Masonry的經常使用功能介紹得差很少了 若是你以爲意猶未盡呢 請下載官方的demo來學習

總而言之 Masonry是一個很是優秀的autolayout庫 可以節省大量的開發和學習時間 尤爲適合我這種純代碼的iOSer

相關文章
相關標籤/搜索