iOS-談一談自適應(yīng)Cell的高度緩存

目錄

  • 系統(tǒng)如何計(jì)算的自適應(yīng)高度?
  • 系統(tǒng)計(jì)算的行高會不會被緩存?
  • 如何緩存?
  • 勘誤

前幾天讀文檔的時(shí)候發(fā)現(xiàn)一對方法

- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize NS_AVAILABLE_IOS(6_0); 
- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority verticalFittingPriority:(UILayoutPriority)verticalFittingPriority NS_AVAILABLE_IOS(8_0);

具體可以參閱《iOS文檔補(bǔ)完計(jì)劃--UIView》中的相關(guān)解釋。

簡而言之這兩個(gè)方法會:

返回Auto Layout后內(nèi)容高度

并且、我們都知道UITableView、如果設(shè)置成rowHeight = UITableViewAutomaticDimension的話。cell的高度將由系統(tǒng)通過Auto Layout自動計(jì)算。


  • 系統(tǒng)如何計(jì)算的自適應(yīng)高度?

而這個(gè)計(jì)算、是否通過上面兩個(gè)方法呢?

經(jīng)過試驗(yàn)、答案是肯定的。

系統(tǒng)調(diào)用的正是- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority verticalFittingPriority:(UILayoutPriority)verticalFittingPriority NS_AVAILABLE_IOS(8_0);這個(gè)方法。


  • 系統(tǒng)計(jì)算的行高會不會被緩存?

經(jīng)過試驗(yàn)、答案是否定的。也就是系統(tǒng)不會緩存計(jì)算過的行高

這里有兩個(gè)能夠讓Cell自適應(yīng)的方式

  1. 對UITableView進(jìn)行設(shè)置
tableView.rowHeight = UITableViewAutomaticDimension
  1. 通過代理返回
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return UITableViewAutomaticDimension;
}

結(jié)果是無論使用哪個(gè)方法、在每次Cell即將被展示的時(shí)候、都會自動調(diào)用上述的systemLayoutSizeFittingSize方法。

兩個(gè)關(guān)鍵的步驟是:
  1. 通過cellForRowAtIndexPath對某個(gè)Cell進(jìn)行配置
    而我們在這一步已經(jīng)將Cell的內(nèi)容配置完畢了
  2. 通過[UITableView _heightForCell:atIndexPath:]計(jì)算Cell高度
    而內(nèi)部則調(diào)用systemLayoutSizeFittingSize獲取具體的高度。

  • 如何緩存?

經(jīng)過以上兩個(gè)探索、我們已經(jīng)知道Cell通過systemLayoutSizeFittingSize高度、并且不會被緩存。

那么、我們需要做的就是自己計(jì)算高度、并且緩存。直接貼一下代碼:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    BSQuestionsModel * model = _dataArray[indexPath.section];
    return model.cell_height?:UITableViewAutomaticDimension;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    BSQuestionsModel * model = _dataArray[indexPath.section];
    BSQuestionsTableViewCell * cell = [BSQuestionsTableViewCell cellForTableView:tableView model:model];
    
    //高度緩存
    CGFloat height = [cell systemLayoutSizeFittingSize:CGSizeMake(tableView.frame.size.width, 0) withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel].height;
    model.cell_height = height;
    
    return cell;
}

這樣、cell在進(jìn)行過一次高度計(jì)算之后。就不需要在計(jì)算第二次了

然后關(guān)于上面的代碼有幾點(diǎn)需要說:
  1. 為什么在cellForRowAtIndexPath里做緩存
    最開始我們已經(jīng)談過了、cellForRowAtIndexPath的調(diào)用在獲取自動布局的高度之前、這樣也能避免重復(fù)取用對應(yīng)位置的Cell。前提是你開啟了預(yù)估行高、具體見下面的解釋。
這里需要補(bǔ)充一下cellForRowAtIndexPathheightForRowAtIndexPath調(diào)用到底誰先誰后:
  1. 如果你的tableView設(shè)置了預(yù)估行高
    cellForRowAtIndexPath永遠(yuǎn)在heightForRowAtIndexPath之前
  2. 如果你的tableView沒有設(shè)置預(yù)估行高
    tableView首先會把所有IndexPath的heightForRowAtIndexPath輪訓(xùn)一遍以計(jì)算contentSize。而后按照情況1的順序。

具體可以參閱《iOS-談?wù)刄ITableView中estimatedRowHeight到底該不該禁用》

所以如果你想不設(shè)置預(yù)估行高、又想用這種方式緩存的話。需要把緩存的代碼寫在heightForRowAtIndexPath而不是cellForRowAtIndexPath

而返回的UITableViewAutomaticDimension主要是為了容錯(cuò)(比如上面的情況)。

  1. 為什么用systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority
    網(wǎng)上很多帖子都這樣寫:
[cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]

但是在我這不太好用、因?yàn)槲襝ell內(nèi)部有一些優(yōu)先級的設(shè)置。

所以、我干脆和系統(tǒng)調(diào)用的方式一樣。

  1. 異步計(jì)算
    是的、我們又可以異步計(jì)算了。雖然我沒寫、因?yàn)槲椰F(xiàn)在得抓緊碼頁面~
關(guān)于一些舊帖子

我搜到的都是14/15年的帖子、和現(xiàn)在的情況感覺還是有出入的。

  1. cell.contentView取出的高度要+1
    網(wǎng)上對+1的解釋是、cellcell.contentView要搞出1個(gè)單位。還附上了兩張圖。

但是現(xiàn)在、cell是比cell.contentView高出0.5(0.5也不一定準(zhǔn)確、xib上有四舍五入的嫌疑)、而不是1。

  1. cell還是用cell.contentView
    我在網(wǎng)上搜了很多帖子、都說要使用cell.contentView
    但是我用cell一樣可以獲取高度。所以用cell唄~

勘誤

不好意思今天是2019.11.29,時(shí)隔一年半了才發(fā)現(xiàn)這個(gè)事。因?yàn)榭戳?a href="http://www.lxweimin.com/p/38b43dff4e93" target="_blank">Bruce_XHG大佬的文章
不過其實(shí)也不算是勘誤、只是發(fā)現(xiàn)個(gè)更好的方法

由于我以前很少使用willDisplayCell這個(gè)方法(其實(shí)現(xiàn)在也是),所以一直沒發(fā)現(xiàn):
實(shí)際上對于自適應(yīng)的cell,只要你在cellForRowAtIndexPath里配置好了,willDisplayCell中返回的cell其實(shí)已經(jīng)被系統(tǒng)計(jì)算過了。
我們可以省去自己計(jì)算的部分,也不需要擔(dān)心自己算的不對。上面的反面教材就不刪了

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    GKTMessageItemModel *model = self.dataArr[indexPath.row];
    UITableViewCell<GKTMessageListTableViewCellProtocol> *cell = [self factoryCellWithTableView:tableView model:model row:indexPath.row];
    
    //這個(gè)頁面對cell與model進(jìn)行了進(jìn)一步的解耦,改的時(shí)候如果對邏輯不清晰,可以參閱。
    //http://www.lxweimin.com/p/723e8435586d#comment-38321201
    [cell configCellWithModel:model indexPath:indexPath];

    return cell;
}

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    GKTMessageItemModel *model = self.dataArr[indexPath.row];
    //高度緩存
    if (model.cell_height == 0) {
        CGFloat height = cell.height;
        model.cell_height = height;
    }
}

最后

本文主要是自己的學(xué)習(xí)與總結(jié)。如果文內(nèi)存在紕漏、萬望留言斧正。如果愿意補(bǔ)充以及不吝賜教小弟會更加感激。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。