在 App 開發中,基本上只要能滑動的 UI 都是 scrollView。
有時候感覺,蘋果這點做的非常不友好。為什么搞的這么麻煩?
在其他的比如 HTML 中,滑動無非就是超過了某個容器的可視范圍,于是就給了一個滑動條。
很自然的一件事情。
到了蘋果這不光需要自己去注意這點。在使用純的 UISCrollView 的時候,我們還需要自己去手動計算可以滾動動的范圍。
吐槽歸吐槽,但 UIScrollView 的 contentSize 還是需要去計算的。
情況一
我們知道 UIScrollView 里面每一個子元素的大小以及子元素的個數。
比如:新特性界面
UIScrollView *sv = [[UIScrollView alloc] init];
sv.contentSize = CGSizeMake(圖片數量 * 圖片的寬, 圖片的高);
這種情況也非常簡單,數字和大小都是確定的。只需要帶入計算即可。
情況二
每一個子視圖的高度固定,但是子視圖的個數不確定。
在這里有就兩種做法了。
第一種,申明一個 height 屬性,每次添加一個視圖的時候,都根據前視圖的高度 & 邊距信息來記錄累加高度。
最后設置成 scrollView 的 contentSize.
UIScrollView *sv1 = [[UIScrollView alloc] init];
CGFloat height2 = 0;
UIView *c1 = [UIView new];
[sv1 addSubview:c1];
sv1.top = 20;
sv1.height = 300;
height2 += 300;
UIView *c2 = [UIView new];
c2.top = 40;
c2.height = 700;
height += 740;
// .........
sv1.contentSize = CGSizeMake(self.view.bounds.size.width, height2);
第二種情況,利用約束。
- 在 scrollView 中添加一個內容容器 containerView。只設置約束 left ,top ,width。
- 然后再這個 containerView 中,添加各種子視圖,并設置子視圖的位置和大小約束。
- 第一個子視圖必須設置 top 約束。
- 最后一個子視圖,必須設置一個 bottom 約束,用來計算 containerView 的 height 屬性。
- 當約束計算完畢之后,containerView 的 大小和位置就確定好了。就可以間接的利用它來設置 scrollView 的 contentSize 了。
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:scrollView];
scrollView.showsHorizontalScrollIndicator = NO;
scrollView.alwaysBounceVertical = YES;
scrollView.backgroundColor = [UIColor whiteColor];
// 先不設置 contentSize.畢竟子視圖有多長還不知道
//scrollView.contentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, xxxx);
/**
不把 scrollView 的 contentSize 寫死,.
而是在其內部放一個uiview.
在 uiview 內部添加子視圖,用約束設置.
根據內部約束的子視圖計算 uiview 的大小.
然后根據 uiview 的大小設置 scrollView 的 contentSize.
*/
/**
現在的做法:
記錄每一個子視圖的高度記憶邊距數據,然后累加起來.
*/
UIView *containerView = [[UIView alloc] init];
// 容器,只設置 x,y,w 高度 h 先不設置,用子視圖的約束去計算
[scrollView addSubview:containerView];
[containerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.offset(0);
make.width.equalTo(scrollView.mas_width);
}];
UIView *childView1 = [[UIView alloc] init];
[containerView addSubview:childView1];
childView1.backgroundColor = [UIColor blueColor];
[childView1 mas_makeConstraints:^(MASConstraintMaker *make) {
// 頭部約束
make.top.offset(20);
make.left.offset(0);
make.width.equalTo(containerView.mas_width);
make.height.offset(300);
}];
UIView *childView2 = [[UIView alloc] init];
[containerView addSubview:childView2];
childView2.backgroundColor = [UIColor greenColor];
[childView2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.offset(0);
make.top.equalTo(childView1.mas_bottom).offset(50);
make.height.offset(400);
make.width.equalTo(containerView.mas_width);
}];
UIView *childView3 = [[UIView alloc] init];
[containerView addSubview:childView3];
childView3.backgroundColor = [UIColor greenColor];
[childView3 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.offset(0);
make.top.equalTo(childView2.mas_bottom).offset(100);
make.width.equalTo(containerView.mas_width);
make.height.offset(700);
// 底部約束,用于計算containerView 的 height
make.bottom.offset(-50);
}];
// 不用去調用約束立即計算的代碼,有個小技巧,使用 dispatch_after 即可拿到約束計算完畢之后的 containerView 的height 值.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%.f",containerView.height);
scrollView.contentSize = CGSizeMake(self.view.width, containerView.height);
});
效果如下:
一個問題?
為什么要在 scrollView 中添加一個 containerView? 而不是直接往 scrollView 中直接添加子視圖?
因為 scrollView 本身的大小已經確定了。它不具備擴展和延伸性。
能夠延伸的是它內部的 contentSize 尺寸下的子視圖。
如果把 子視圖直接添加到 scrollView ,并添加約束,那么大小范圍,之后 scrollView 的 frame 那么大。
且子視圖可能會重疊在一起,甚至會出現約束沖突。
但是如果,在scrollView 內部添加一個 containerView 那么,它的大小就可以不確定了。只用約束設置 left top width 。高度則是由其內部的第一個子視圖的 top 約束以及最后一個子視圖的 bottom 約束計算得出即可。
最后在使用 dispatch_after 小技巧,不需要提前計算約束,就可以把 containerView 的 height 賦值給 scrollView 的 contentSize。