自動適應cell的高度

1. 如何自動適應cell的高度

在IOS的佈局中,計算和適應cell的高度是個經典的問題, 在frame時代,我們都知道用sizeWithFont: 先計算出文字的高度,然後通過計算得出cell的高度,然後賦予heightForRow:

那在Autolayout時代如何計算cell的高度呢?因爲sizeWithFont:方法已經不太實用了。其實Autolayout不但更簡單,還可以不用寫過多的計算代碼達到自適應高度。

理論上是可以通過已知的完整的Constraints和view的屬性來計算高度的,我們可以通過systemLayoutSizeFittingSize:方法來獲取計算出來cell的size,我們知道cell的高度需要在tableView的代理方法tableView:heightForRowAtIndexPath:中實現的,那麼我們考慮從以下兩點來做:

  • 通過創建一個額外的cell專門用來計算其高度
  • 因爲計算需要佈局,所以儘量讓其只計算一次,計算完可以將高度保存起來

基於這兩個要點,我們可以嘗試如下(僞代碼):

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
- (CGFloat)autoAdjustedCellHeightAtIndexPath:(NSIndexPath *)indexPath inTableView:(UITableView *)tableView {

    CGFloat cellHeight = [self cellHeightAtIndexPath:indexPath];
    if (cellHeight > 0) {
        return cellHeight;
    } else {
      //此方法創建用於自適應的cell, 注意創建一個就夠了
        UITableViewCell *cell = [self cellForTableViewAutoAdjust];

        //更新cell,同cellForRow中更新一致
        [self updateCell:cell]

        //讓cell進行layout
        [cell setNeedsUpdateConstraints];
        [cell updateConstraintsIfNeeded];
        cell = CGRectMake(0.0f, 0.0f, CGRectGetWidth(tableView.bounds), CGRectGetHeight(layoutGuideView.bounds));
        [cell setNeedsLayout];
        [cell layoutIfNeeded];

        CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

        height += 1.0f;
        //計算完後保存,避免多次重複計算
        [self saveCellHeight:height forIndexPath:indexPath];
        return height;
    }
}

經過測試,工作良好,在體會到這點好處之後就發現Autolayout的好處了,它其實是讓佈局變得簡單了。

在完成這個自動適應高度的設計過程中要注意幾點:

  1. cell中的多行UILabel,如果其width不是固定的話(比如屏幕尺寸不同,width不同),要手動設置其preferredMaxLayoutWidth。 因爲計算UILabel的intrinsicContentSize需要預先確定其width纔行。

  2. 要保證垂直方向上的Constraints理論上可以計算出cell的高度,另:最好調低其中一個Constraint的優先級,避免約束髮生衝突

  3. tableView的dequeueReusableCellWithIdentifier取出來的cell如果未用於tableView, 那麼它就leak了. 因此用於計算高度的cell不要從此方法獲取。

2. 如何在scrollView中使用Autolayout

scrollView比較特殊,因爲它有個contentSize的屬性。那麼在遇到scrollView時,怎麼使用Autolayout呢。其實關鍵點就一點:

ScrollView的contentSize的大小是由其subview的constraints來決定的。

知道這一點其實就夠了,那麼基於這個特性,在應用的時候要注意哪些呢?

  • 完全依賴scrollView來計算subview的座標的Constraints設法不行了,計算條件互相依賴的,常見的如left+right+top+bottom不行了。
  • 要保證contentSize可以通過subview的constraints能夠計算出來。

舉個栗子:

1
2
3
4
5
6
7
8
[childView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.width.mas_equalTo(scrollView.mas_width);
  make.height.mas_equalTo(scrollView.mas_height);
  make.leading.mas_equalTo(scrollView.mas_leading);
  make.top.mas_equalTo(scrollView.mas_top);
  make.bottom.mas_equalTo(scrollView.mas_bottom);
  make.trailing.mas_equalTo(scrollView.mas_trailing).multipliedBy(1./4.);
}];

例子中如果scrollView的size是(100x100), 那麼contentSize即爲(400x100)

3. Autolayout時的動畫

在使用Autolayout時,動畫的使用和以前也不同了,以前我們是修改frame,現在我們可以通過修改Constraints, 然後在動畫時layoutIfNeeded就行了。

1
2
3
[UIView animateWithDuration:0.2 animations:^{
    [view layoutIfNeeded];
}];

Autolayout有時在動畫時候會很方便,因爲View之間的座標是相互影響的,在傳統frame中,如果改變一個view的frame,那麼可能你要更改很多view的frame,才能讓頁面顯得和諧。在Autolayout中可能只需要修改一個Constraint就可以了,在做動畫時會很方便。

4. Autolayout在IOS6上的坑

雖然Autolayout非常強大,但是在剛出現的版本IOS6上,還是有一些坑的。在IOS6上你會發現在某些系統控件上如: UITableViewCellUITableView 等view上直接添加Constraints會crash。 衝突的Constraints會直接導致crash等。

在strackoverflow有回答上說明了爲什麼UITableViewCell等部分系統控件上添加Constraints會crash, 即UITableViewCell等部分控件的layoutSubviews裏面沒有執行[super layoutSubviews]

所以想要使用Autolayout的同學們,如果你要兼容IOS6的話,有些問題還是要重視的,需要充分的測試哦。

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