關于 UISCrollView 的 contentSize 計算的兩種方法。

在 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 的 contentSize.gif

一個問題?
為什么要在 scrollView 中添加一個 containerView? 而不是直接往 scrollView 中直接添加子視圖?

因為 scrollView 本身的大小已經確定了。它不具備擴展和延伸性。
能夠延伸的是它內部的 contentSize 尺寸下的子視圖。
如果把 子視圖直接添加到 scrollView ,并添加約束,那么大小范圍,之后 scrollView 的 frame 那么大。
且子視圖可能會重疊在一起,甚至會出現約束沖突。

但是如果,在scrollView 內部添加一個 containerView 那么,它的大小就可以不確定了。只用約束設置 left top width 。高度則是由其內部的第一個子視圖的 top 約束以及最后一個子視圖的 bottom 約束計算得出即可。

最后在使用 dispatch_after 小技巧,不需要提前計算約束,就可以把 containerView 的 height 賦值給 scrollView 的 contentSize。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容