Masonry

Masonry中的坑數組

在使用Masonry進行約束時,有一些是須要注意的。less

  1. 在使用Masonry添加約束以前,須要在addSubview以後才能使用,不然會致使崩潰。函數

  2. 在添加約束時初學者常常會出現一些錯誤,約束出現問題的緣由通常就是兩種:約束衝突和缺乏約束。對於這兩種問題,能夠經過調試和log排查。佈局

  3. 以前使用Interface Builder添加約束,若是約束有錯誤直接就能夠看出來,而且會以紅色或者黃色警告體現出來。而Masonry則不會直觀的體現出來,而是以運行過程當中崩潰或者打印異常log體現,因此這也是手寫代碼進行AutoLayout的一個缺點。性能

這個問題只能經過多敲代碼,積攢純代碼進行AutoLayout的經驗,慢慢就用起來愈來愈駕輕就熟了。測試

Masonry基礎使用網站

Masonry基礎APIui

1
2
3
4
5
6
7
8
9
mas_makeConstraints()    添加約束
mas_remakeConstraints()  移除以前的約束,從新添加新的約束
mas_updateConstraints()  更新約束
 
equalTo()       參數是對象類型,通常是視圖對象或者mas_width這樣的座標系對象
mas_equalTo()   和上面功能相同,參數能夠傳遞基礎數據類型對象,能夠理解爲比上面的API更強大
 
width()         用來表示寬度,例如表明view的寬度
mas_width()     用來獲取寬度的值。和上面的區別在於,一個表明某個座標系對象,一個用來獲取座標系對象的值

Auto Boxingspa

上面例如equalTo或者width這樣的,有時候須要涉及到使用mas_前綴,這在開發中須要注意做區分。代理

若是在當前類引入#import "Masonry.h"以前,用下面兩種宏定義聲明一下,就不須要區分mas_前綴。

1
2
3
4
// 定義這個常量,就能夠不用在開發過程當中使用"mas_"前綴。
#define MAS_SHORTHAND
// 定義這個常量,就可讓Masonry幫咱們自動把基礎數據類型的數據,自動裝箱爲對象類型。
#define MAS_SHORTHAND_GLOBALS

修飾語句

Masonry爲了讓代碼使用和閱讀更容易理解,因此直接經過點語法就能夠調用,還添加了and和with兩個方法。這兩個方法內部實際上什麼都沒幹,只是在內部將self直接返回,功能就是爲了更加方便閱讀,對代碼執行沒有實際做用。

例以下面的例子:

1
make.top.and.bottom.equalTo(self.containerView). with .offset(padding);

其內部代碼實現,實際上就是直接將self返回。

1
2
3
- (MASConstraint *) with  {
     return  self;
}

更新約束和佈局

關於更新約束佈局相關的API,主要用如下四個API:

1
2
3
4
- (void)updateConstraintsIfNeeded  調用此方法,若是有標記爲須要從新佈局的約束,則當即進行從新佈局,內部會調用updateConstraints方法
- (void)updateConstraints          重寫此方法,內部實現自定義佈局過程
- (BOOL)needsUpdateConstraints     當前是否須要從新佈局,內部會判斷當前有沒有被標記的約束
- (void)setNeedsUpdateConstraints  標記須要進行從新佈局

關於UIView從新佈局相關的API,主要用如下三個API:

1
2
3
- (void)setNeedsLayout  標記爲須要從新佈局
- (void)layoutIfNeeded  查看當前視圖是否被標記須要從新佈局,有則在內部調用layoutSubviews方法進行從新佈局
- (void)layoutSubviews  重寫當前方法,在內部完成從新佈局操做

Masonry示例代碼

Masonry本質上就是對系統AutoLayout進行的封裝,包括裏面不少的API,都是對系統API進行了一次二次包裝。

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef NS_OPTIONS(NSInteger, MASAttribute) {
     MASAttributeLeft = 1 << NSLayoutAttributeLeft,
     MASAttributeRight = 1 << NSLayoutAttributeRight,
     MASAttributeTop = 1 << NSLayoutAttributeTop,
     MASAttributeBottom = 1 << NSLayoutAttributeBottom,
     MASAttributeLeading = 1 << NSLayoutAttributeLeading,
     MASAttributeTrailing = 1 << NSLayoutAttributeTrailing,
     MASAttributeWidth = 1 << NSLayoutAttributeWidth,
     MASAttributeHeight = 1 << NSLayoutAttributeHeight,
     MASAttributeCenterX = 1 << NSLayoutAttributeCenterX,
     MASAttributeCenterY = 1 << NSLayoutAttributeCenterY,
     MASAttributeBaseline = 1 << NSLayoutAttributeBaseline,
};

經常使用方法

設置內邊距

1
2
3
4
5
6
7
8
9
10
11
12
13
/** 
  設置yellow視圖和self.view等大,而且有10的內邊距。
  注意根據UIView的座標系,下面right和bottom進行了取反。因此不能寫成下面這樣,不然right、bottom這兩個方向會出現問題。
  make.edges.equalTo(self.view).with.offset(10);
 
  除了下面例子中的offset()方法,還有針對不一樣座標系的centerOffset()、sizeOffset()、valueOffset()之類的方法。
  */
[self.yellowView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.left.equalTo(self.view). with .offset(10);
     make.top.equalTo(self.view). with .offset(10);
     make.right.equalTo(self.view). with .offset(-10);
     make.bottom.equalTo(self.view). with .offset(-10);
}];

經過insets簡化設置內邊距的方式

1
2
3
4
5
// 下面的方法和上面例子等價,區別在於使用insets()方法。
[self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {
     // 下、右不須要寫負號,insets方法中已經爲咱們作了取反的操做了。
     make.edges.equalTo(self.view). with .insets(UIEdgeInsetsMake(10, 10, 10, 10));
}];

更新約束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 設置greenView的center和size,這樣就能夠達到簡單進行約束的目的
[self.greenView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.center.equalTo(self.view);
     // 這裏經過mas_equalTo給size設置了基礎數據類型的參數,參數爲CGSize的結構體
     make.size.mas_equalTo(CGSizeMake(300, 300));
}];
 
// 爲了更清楚的看出約束變化的效果,在顯示兩秒後更新約束。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
     [self.greenView mas_updateConstraints:^(MASConstraintMaker *make) {
         make.centerX.equalTo(self.view).offset(100);
         make.size.mas_equalTo(CGSizeMake(100, 100));
     }];
});

大於等於和小於等於某個值的約束

1
2
3
4
5
6
7
[self.textLabel mas_makeConstraints:^(MASConstraintMaker *make) {
     make.center.equalTo(self.view);
     // 設置寬度小於等於200
     make.width.lessThanOrEqualTo(@200);
     // 設置高度大於等於10
     make.height.greaterThanOrEqualTo(@(10));
}];

self.textLabel.text = @"這是測試的字符串。能看到一、二、3個步驟,第一步固然是上傳照片了,要上傳正面近照哦。上傳後,網站會自動識別你的面部,若是以爲識別的不許,你還能夠手動修改一下。左邊能夠看到16項修改參數,最上面是總體修改,你也能夠根據本身的意願單獨修改某項,將鼠標放到選項上面,右邊的預覽圖會顯示相應的位置。";

textLabel只須要設置一個屬性便可

1
self.textLabel.numberOfLines = 0;

使用基礎數據類型當作參數

1
2
3
4
5
6
7
8
9
10
11
12
/** 
  若是想使用基礎數據類型當作參數,Masonry爲咱們提供了"mas_xx"格式的宏定義。
  這些宏定義會將傳入的基礎數據類型轉換爲NSNumber類型,這個過程叫作封箱(Auto Boxing)。
  
  "mas_xx"開頭的宏定義,內部都是經過MASBoxValue()函數實現的。
  這樣的宏定義主要有四個,分別是mas_equalTo()、mas_offset()和大於等於、小於等於四個。
  */
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.center.equalTo(self.view);
     make.width.mas_equalTo(100);
     make.height.mas_equalTo(100);
}];

設置約束優先級

1
2
3
4
5
6
7
8
9
10
11
/** 
  Masonry爲咱們提供了三個默認的方法,priorityLow()、priorityMedium()、priorityHigh(),這三個方法內部對應着不一樣的默認優先級。
  除了這三個方法,咱們也能夠本身設置優先級的值,能夠經過priority()方法來設置。
  */
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.center.equalTo(self.view);
     make.width.equalTo(self.view).priorityLow();
     make.width.mas_equalTo(20).priorityHigh();
     make.height.equalTo(self.view).priority(200);
     make.height.mas_equalTo(100).priority(1000);
}];
1
2
3
4
5
6
Masonry也幫咱們定義好了一些默認的優先級常量,分別對應着不一樣的數值,優先級最大數值是1000。
static const MASLayoutPriority MASLayoutPriorityRequired = UILayoutPriorityRequired;
static const MASLayoutPriority MASLayoutPriorityDefaultHigh = UILayoutPriorityDefaultHigh;
static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 500;
static const MASLayoutPriority MASLayoutPriorityDefaultLow = UILayoutPriorityDefaultLow;
static const MASLayoutPriority MASLayoutPriorityFittingSizeLevel = UILayoutPriorityFittingSizeLevel;

設置約束比例

1
2
3
4
5
6
// 設置當前約束值乘以多少,例如這個例子是redView的寬度是self.view寬度的0.2倍。
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.center.equalTo(self.view);
     make.height.mas_equalTo(30);
     make.width.equalTo(self.view).multipliedBy(0.2);
}];

小練習

子視圖等高練習

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/** 
  下面的例子是經過給equalTo()方法傳入一個數組,設置數組中子視圖及當前make對應的視圖之間等高。
 
  須要注意的是,下面block中設置邊距的時候,應該用insets來設置,而不是用offset。
  由於用offset設置right和bottom的邊距時,這兩個值應該是負數,因此若是經過offset來統一設置值會有問題。
  */
CGFloat padding = LXZViewPadding;
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.left.right.top.equalTo(self.view).insets(UIEdgeInsetsMake(padding, padding, 0, padding));
     make.bottom.equalTo(self.blueView.mas_top).offset(-padding);
}];
 
[self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.left.right.equalTo(self.view).insets(UIEdgeInsetsMake(0, padding, 0, padding));
     make.bottom.equalTo(self.yellowView.mas_top).offset(-padding);
}];
 
/** 
  下面設置make.height的數組是關鍵,經過這個數組能夠設置這三個視圖高度相等。其餘例如寬度之類的,也是相似的方式。
  */
[self.yellowView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.left.right.bottom.equalTo(self.view).insets(UIEdgeInsetsMake(0, padding, padding, padding));
     make.height.equalTo(@[self.blueView, self.redView]);
}];

子視圖垂直居中練習

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/** 
  要求:(這個例子是在其餘人博客裏看到的,而後按照要求本身寫了下面這段代碼)
  兩個視圖相對於父視圖垂直居中,而且兩個視圖以及父視圖之間的邊距均爲10,高度爲150,兩個視圖寬度相等。
  */
CGFloat padding = 10.f;
[self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.centerY.equalTo(self.view);
     make.left.equalTo(self.view).mas_offset(padding);
     make.right.equalTo(self.redView.mas_left).mas_offset(-padding);
     make.width.equalTo(self.redView);
     make.height.mas_equalTo(150);
}];
 
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.centerY.equalTo(self.view);
     make.right.equalTo(self.view).mas_offset(-padding);
     make.width.equalTo(self.blueView);
     make.height.mas_equalTo(150);
}];

UITableView動態Cell高度

在iOS UI開發過程當中,UITableView的動態Cell高度一直都是個問題。實現這樣的需求,實現方式有不少種,只是實現起來複雜程度和性能的區別。

在不考慮性能的狀況下,tableView動態Cell高度,能夠採起估算高度的方式。若是經過估算高度的方式實現的話,不管是純代碼仍是Interface Builder,都只須要兩行代碼就能夠完成Cell自動高度適配。

實現方式:

須要設置tableView的rowHeight屬性,這裏設置爲自動高度,告訴系統Cell的高度是不固定的,須要系統幫咱們進行計算。而後設置tableView的estimatedRowHeight屬性,設置一個估計的高度。(我這裏用的代理方法,實際上都同樣)

原理:

這樣的話,在tableView被建立以後,系統會根據estimatedRowHeight屬性設置的值,爲tableView設置一個估計的值。而後在Cell顯示的時候再獲取Cell的高度,並刷新tableView的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
- (void)tableViewConstraints {
     [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
         make.edges.equalTo(self.view);
     }];
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
     return  self.dataList.count;
}
 
- (MasonryTableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
     MasonryTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:LXZTableViewCellIdentifier];
     [cell reloadViewWithText:self.dataList[indexPath.row]];
     return  cell;
}
 
// 須要注意的是,這個代理方法和直接返回當前Cell高度的代理方法並不同。
// 這個代理方法會將當前全部Cell的高度都預估出來,而不是隻計算顯示的Cell,因此這種方式對性能消耗仍是很大的。
// 因此經過設置estimatedRowHeight屬性的方式,和這種代理方法的方式,最後性能消耗都是同樣的。
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
     return  50.f;
}
 
- (UITableView *)tableView {
     if  (!_tableView) {
         _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
         _tableView.delegate = self;
         _tableView.dataSource = self;
         // 設置tableView自動高度
         _tableView.rowHeight = UITableViewAutomaticDimension;
         [_tableView registerClass:[MasonryTableViewCell class] forCellReuseIdentifier:LXZTableViewCellIdentifier];
         [self.view addSubview:_tableView];
     }
     return  _tableView;
}

UIScrollView自動佈局

以前聽不少人說過UIScrollView很麻煩,然而我並無感受到有多麻煩(並不是裝逼)。我感受說麻煩的人可能根本就沒試過吧,只是以爲很麻煩而已。

我這裏就講一下兩種進行UIScrollView自動佈局的方案,而且會講一下自動佈局的技巧,只要掌握技巧,佈局其實很簡單。

佈局小技巧:

給UIScrollView添加的約束是定義其frame,設置contentSize是定義其內部大小。UIScrollView進行addSubview操做,都是將其子視圖添加到contentView上。

因此,添加到UIScrollView上的子視圖,對UIScrollView添加的約束都是做用於contentView上的。只須要按照這樣的思路給UIScrollView設置約束,就能夠掌握設置約束的技巧了。

提早設置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
// 提早設置好UIScrollView的contentSize,並設置UIScrollView自身的約束
self.scrollView.contentSize = CGSizeMake(1000, 1000);
[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.edges.equalTo(self.view);
}];
 
// 雖然redView的get方法內部已經執行過addSubview操做,可是UIView始終以最後一次添加的父視圖爲準,也就是redView始終是在最後一次添加的父視圖上。
[self.scrollView addSubview:self.redView];
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.left.top.equalTo(self.scrollView);
     make.width.height.mas_equalTo(200);
}];
 
[self.scrollView addSubview:self.blueView];
[self.blueView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.left.equalTo(self.redView.mas_right);
     make.top.equalTo(self.scrollView);
     make.width.height.equalTo(self.redView);
}];
 
[self.scrollView addSubview:self.greenView];
[self.greenView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.left.equalTo(self.scrollView);
     make.top.equalTo(self.redView.mas_bottom);
     make.width.height.equalTo(self.redView);
}];

自動contentSize

上面的例子是提早設置好UIScrollView的contentSize的內部size,而後直接向裏面addSubview。可是這有個要求就是,須要提早知道contentSize的大小,否則無法設置。

這個例子中將會展現動態改變contentSize的大小,內部視圖有多少contentSize就自動擴充到多大。

這種方式的實現,主要是依賴於建立一個containerView內容視圖,並添加到UIScrollView上做爲子視圖。UIScrollView原來的子視圖都添加到containerView上,而且和這個視圖設置約束。

由於對UIScrollView進行addSubview操做的時候,本質上是往其contentView上添加。也就是containerView的父視圖是contentView,經過containerView撐起contentView視圖的大小,以此來實現動態改變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
// 在進行約束的時候,要對containerView的上下左右都添加和子視圖的約束,以便確認containerView的邊界區域。
[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.edges.equalTo(self.view);
}];
 
CGFloat padding = LXZViewPadding;
[self.containerView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.edges.equalTo(self.scrollView).insets(UIEdgeInsetsMake(padding, padding, padding, padding));
}];
 
[self.containerView addSubview:self.greenView];
[self.greenView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.top.left.equalTo(self.containerView).offset(padding);
     make.size.mas_equalTo(CGSizeMake(250, 250));
}];
 
[self.containerView addSubview:self.redView];
[self.redView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.top.equalTo(self.containerView).offset(padding);
     make.left.equalTo(self.greenView.mas_right).offset(padding);
     make.size.equalTo(self.greenView);
     make.right.equalTo(self.containerView).offset(-padding);
}];
 
[self.containerView addSubview:self.yellowView];
[self.yellowView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.left.equalTo(self.containerView).offset(padding);
     make.top.equalTo(self.greenView.mas_bottom).offset(padding);
     make.size.equalTo(self.greenView);
     make.bottom.equalTo(self.containerView).offset(-padding);
}];
相關文章
相關標籤/搜索