1. 程式人生 > >四種方法實現UITableView的cell高度自動計算

四種方法實現UITableView的cell高度自動計算

具體實現程式碼

iOS8時代的高度計算非常簡單,下面兩行程式碼就搞定了,非常方便。
前提是需要設定好在垂直高度上的約束。

- (void)viewDidLoad{    
    self.tableView.estimatedRowHeight = 80.0f;
    self.tableView.rowHeight = UITableViewAutomaticDimension;
}

效果如下
                  

方法2:iOS6的系統API結合autolayout

控制元件的約束和第一個方法的一樣,下面列出的程式碼是和第一個方法不同的地方。
該方法的demo和第一個方法的demo是同一個,每個方法獨立使用到的程式碼我會特別註明,沒有註明就是所有方法共有的。

//TableViewCell.m檔案
//======================
- (void)setModel:(TableViewModel *)model
{
    //必須設定label的最大寬度,不然系統無法計算label的最大高度
    CGFloat preferredWidth = [UIScreen mainScreen].bounds.size.width - 53;
    self.userName.preferredMaxLayoutWidth = preferredWidth;
    self.userContentString.preferredMaxLayoutWidth = preferredWidth;

    self.headImage.image = model.userHeadImage;
    self.userContentImage.image = model.userContentImage;
    self.userContentString.text = model.userContentString;
    self.userName.text = model.userName;
}
//viewController.m檔案
//===========================
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    static TableViewCell *Cell;
    static dispatch_once_t onceToken;
    //必須使用dispatch_once,保證只會從快取池中取一個cell用於高度計算,其他的cell高度都是用這個cell的高度。不然每次都從快取池中取出來不同的cell,導致高度計算出問題
    dispatch_once(&onceToken, ^{
        Cell = [tableView dequeueReusableCellWithIdentifier:CellId];
    });

   TableViewModel *model = self.modelArray[indexPath.row];
    Cell.model = model;
    // 根據當前資料,計算Cell的高度,注意+1是contentview和cell之間的分割線高度
    model.cellHeight = [Cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height +1.0f;
   return model.cellHeight;
}

//實現該方法後,tableview就不會一次性呼叫完所有cell的高度,有些不在可見範圍的cell是不需要一開始就知道高度的。當然,estimatedHeightForRowAtIndexPath方法呼叫頻率就會非常高,所以我們儘量返回一個比較接近實際結果的固定值以提高效能.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 112.0f;
}

該方法實現效果和方法一相同

方法3、手動計算

該方法需要手動計算垂直高度上每個控制元件的高度,然後相加得出cell的高度。
這種方法最繁瑣,但是也是最精確的,也是最可控的。
使用這個方法,可以不需要使用autolayout設定約束,直接使用frame設定每個控制元件的位置。但是為了方便,我這裡還是使用autolayout設定控制元件的約束和位置。

因為需要確切的知道每個控制元件的高度,所以這裡image的高度必須是固定的,這樣才可以進行cell的高度計算
修改如下

修改的程式碼如下:

//TableViewModel.m檔案
//===============================
#import "TableViewModel.h"

@implementation TableViewModel

//方法3程式碼
- (CGFloat)cellHeight{
    // 文字的最大尺寸(設定內容label的最大size,這樣才可以計算label的實際高度,需要設定最大寬度,但是最大高度不需要設定,只需要設定為最大浮點值即可),53為內容label到cell左邊的距離
    CGSize maxSize = CGSizeMake([UIScreen mainScreen].bounds.size.width - 53, MAXFLOAT);

    // 計算內容label的高度
    CGFloat textH = [self.userContentString boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:14]} context:nil].size.height;

    /*
     暱稱label和cell的頂部為0
     17為暱稱label的高度
     8.5為暱稱label和內容label的間距
     textH為內容label的高度
     304為內容image的高度
     */
    _cellHeight = 0 + 17 + 8.5 + 8 +textH + 304;

    return _cellHeight;
}

//ViewController.m檔案
//==========================

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
 //方法3程式碼
   TableViewModel *model =  self.modelArray[indexPath.row];
    return model.cellHeight;
}


方法四、使用第三方框架

這是國內的一個大神寫的框架,可以一行程式碼就實現cell的高度自動計算。同時還能實現快取高度,最低相容版本為iOS6。
實現程式碼就不寫了,非常簡單

這裡囉嗦兩點,也是自己踩過的坑:

1、在博主的文字裡面提到使用的時候直接使用如下程式碼即可:

#import <UITableView+FDTemplateLayoutCell.h>
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return [tableView fd_heightForCellWithIdentifier:@"identifer" cacheByIndexPath:indexPath configuration:^(id cell) {
        // 配置 cell 的資料來源,和 "cellForRow" 乾的事一致,比如:
        cell.entity = self.feedEntities[indexPath.row];
    }];
}

一定要把上面的id cell,換成自己的cell類,比如我的就是WSTableViewCell *cell。算是一個小坑吧。

2、很多人肯定吃過self-sizing-cell的虧,覺得我上面都設定對了,為什麼就是算不出來高度呢?

要滿足self-sizing-cell,必須滿足兩點:

  • 你的cell裡面的控制元件必須在上下左右四個方向都有約束到cell的四個邊。如下圖:

*約束一定要是控制元件和cell的contentView邊緣之間的約束,而不是控制元件和cell邊緣的之間的約束。

因為設計給的圖,cell內部的控制元件和cell的距離是到cell邊緣的距離,然後我就發現怎麼都不能進行高度自動計算,所有的cell全部疊在一起了。被這個坑了很久,一直找不出來原因。另外cell的邊緣和cell的contentView的邊緣相差8pt。

錯誤設定:

ReplayCell是cell的名字

正確設定:

superView是cell的contentView

總結:

上面四種方法各有優缺點,如果你的App最低相容版本是iOS8,那請毫不猶豫的選擇方法一的系統方法吧,高效簡潔。

如果你需要最低相容iOS6,可以從其他三個方法中選一個,建議使用方法四的第三方框架,高效強大。

所有的程式碼都放在了Github上面,小夥伴們可以參考下。

地址如下:
https://github.com/XiMu-Demo/Blog-Demo/tree/master/calculateCellHeight