【iOS學習筆記 15-12-31】 UIScrollview與Autolayout的那點事

原文轉載自:http://adad184.com/2015/12/01/scrollview-under-autolayout/


UIScrollview與Autolayout的那點事

前言


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

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? 不是多此一舉嗎?

那麼今天我就按照我的理解來說明一下這個問題

梳理


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

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

換成代碼 是這個樣子

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的計算

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

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

示例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);
}];

效果

這裏我建立了一個寬等於scrollview 高等於scrollview高度1.5倍的view 然後scrollview成功的計算出了ContentSize

關鍵就在於

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其實是這個問題上的最佳實踐


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章