Masonry裏UIScrollView的坑

自從寫了介紹Masonry那篇文章之後 就一直有人對UIScrollView的那個例子不是很理解 git

1
2
3
4
5
6
UIView *container = [UIView new];
[scrollView addSubview:container];
[container mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(scrollView);
make.width.equalTo(scrollView);
}];
  • 爲何要用一個container包含其餘subview?
  • 爲何指定了edges 還要指定width? 不是畫蛇添足嗎?

那麼今天我就按照個人理解來講明一下這個問題github

梳理


直入主題 要解釋以前的問題 最重要的一個概念就是app

UIScrollView依靠與其subviews之間的約束來肯定ContentSize的大小iphone

換成代碼 是這個樣子佈局

1
2
3
4
5
6
[scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(v1.mas_left);
make.right.equalTo(v1.mas_right);
make.top.equalTo(v1.mas_top);
make.bottom.equalTo(v1.mas_bottom);
}];

 

這是由於UIScrollView是個很是特殊的view UIScrollView與其subview之間相對位置的約束 並不會直接用於frame的計算 而是會轉化爲對ContentSize的計算ui

換句話說 當UIScrollView知道了上下左右的約束分別指向subview什麼位置以後 只要subview的位置固定下來了 ContentSize的大小就肯定下來了 spa

下面來個簡單的例子 強烈建議配合demo來理解下面的例子(demo的連接在文尾)
請點擊->在線演示 (爲了方便理解 我將ContentSize用紅線框了出來 另外爲了查看ContentSize 我把UIScrollView的clipTobounds關閉了 能夠經過左上角的開關來切換實際的效果)code

示例1

1
2
3
4
5
[v1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(scrollView);
make.width.equalTo(scrollView);
make.height.equalTo(scrollView).multipliedBy(1.5);
}];

效果blog

這裏我創建了一個寬等於scrollview 高等於scrollview高度1.5倍的view 而後scrollview成功的計算出了ContentSizeip

關鍵就在於

1
make.edges.equalTo(scrollView);

 

這句話其實等同與以前我提到的

1
2
3
4
5
6
[scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(v1.mas_left);
make.right.equalTo(v1.mas_right);
make.top.equalTo(v1.mas_top);
make.bottom.equalTo(v1.mas_bottom);
}];

 

scrollview由於上面的約束 會以v1的大小來計算ContentSize

示例2

若是嘗試改變v1的大小 會怎麼樣呢?

1
2
3
4
5
[v1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(scrollView);

make.size.equalTo(scrollView).sizeOffset(CGSizeMake(80, 80));
}];

效果

能看到 當我僅改變v1的大小 而不變其餘的東西的狀況下 scrollview的ContentSize也是隨着v1的大小變化而變化的

示例3

接下來示例就會稍微複雜點 若是同時有兩個view 會如何呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[v1 mas_makeConstraints:^(MASConstraintMaker *make) {

make.left.top.right.equalTo(scrollView).insets(UIEdgeInsetsMake(10, 10, 0, 10));

make.width.equalTo(scrollView).multipliedBy(1.1);
make.bottom.equalTo(v2.mas_top).offset(-50);
make.height.equalTo(@200);
}];

[v2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(scrollView);

make.left.right.equalTo(v1).insets(UIEdgeInsetsMake(0, 50, 0, 50));
make.height.equalTo(@250);
}];

 

效果

這個例子中 scrollview的四個方向的約束並無放在同一個subview上 而是分別指向了兩個view 因此scrollview的ContentSize會根據兩個view之間的約束來肯定

示例4

若是將四個方向的約束分別放到四個不一樣的view上面 會怎麼樣呢?

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
CGSize size = CGSizeMake(200, 200);

[v1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(scrollView.mas_top);

make.size.mas_equalTo(size);
}];

[v2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(scrollView.mas_left);

make.size.mas_equalTo(size);
make.right.equalTo(v1.mas_left);
make.top.equalTo(v1.mas_bottom);
}];

[v3 mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(scrollView.mas_right);

make.size.mas_equalTo(size);
make.left.equalTo(v1.mas_right);
make.top.equalTo(v1.mas_bottom);
}];

[v4 mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(scrollView.mas_bottom);

make.size.mas_equalTo(size);
make.left.equalTo(v1.mas_left);
make.top.equalTo(v2.mas_bottom);
}];

效果

將四個方向的約束分別指向四個view的中心點 咱們也能獲得正確的ContentSize

若是你看懂了示例4的代碼與效果 相信你對這個問題的全部疑惑都應該已經解除了

那麼再回到最開始那個問題

1
2
make.edges.equalTo(scrollView);
make.width.equalTo(scrollView);

 

通常狀況下咱們使用UIScrollView來進行autolayout佈局 都是爲了實現相似Android中的線性佈局(有不少雜的非重複性的subview 若是使用UITableView和UICollectionView太麻煩) 這時直接使用UIScrollView就會很靈活

那麼
若是咱們須要豎向的滑動 就把width設爲和scrollview相同
若是須要橫向的滑動 就把height設爲和scrollview相同

就是這麼簡單

小結


源碼和Demo請點這裏

前不久@nixzhu也寫了一篇關於UIScrollView的文章 而後我在微博上回復說」使用一個單一的containerView佔滿所有,而後把全部的subview添加到containerView中「 不過nixzh 表示他是極力避免這樣的 可是後在這個問題上 我是極力推薦這樣使用的 

就如同示例1和示例2同樣 若是你須要添加subview 你只要簡單的添加到v1上 並添加與v1的約束 就能夠得到正確的ContentSize了

若是不這樣作 就相似示例3和示例4 這些邊界約束都須要一個一個的設置 這實際上是沒有必要的

使用單一的containerView實際上是這個問題上的最佳實踐

相關文章
相關標籤/搜索