來源:裏脊串的開發隨筆git
前言github
自從寫了介紹Masonry那篇文章之後 就一直有人對UIScrollView的那個例子不是很理解app
UIView *container = [UIView
new
];
[scrollView addSubview:container];
[container mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(scrollView);
make.width.equalTo(scrollView);
}];
爲何要用一個container包含其餘subview?iphone
爲何指定了edges 還要指定width? 不是畫蛇添足嗎?佈局
那麼今天我就按照個人理解來講明一下這個問題ui
梳理spa
直入主題 要解釋以前的問題 最重要的一個概念就是code
UIScrollView依靠與其subviews之間的約束來肯定ContentSize的大小blog
換成代碼 是這個樣子ip
[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
[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
關鍵就在於
make.edges.equalTo(scrollView);
這句話其實等同與以前我提到的
[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的大小 會怎麼樣呢?
[v1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(scrollView);
make.size.equalTo(scrollView).sizeOffset(CGSizeMake(80, 80));
}];
效果
能看到 當我僅改變v1的大小 而不變其餘的東西的狀況下 scrollview的ContentSize也是隨着v1的大小變化而變化的
示例3
接下來示例就會稍微複雜點 若是同時有兩個view 會如何呢?
[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上面 會怎麼樣呢?
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的代碼與效果 相信你對這個問題的全部疑惑都應該已經解除了
那麼再回到最開始那個問題
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實際上是這個問題上的最佳實踐