- 系統(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)的方式
- 對UITableView進(jìn)行設(shè)置
tableView.rowHeight = UITableViewAutomaticDimension
- 通過代理返回
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
結(jié)果是無論使用哪個(gè)方法、在每次Cell即將被展示的時(shí)候、都會自動調(diào)用上述的systemLayoutSizeFittingSize
方法。
兩個(gè)關(guān)鍵的步驟是:
- 通過
cellForRowAtIndexPath
對某個(gè)Cell進(jìn)行配置
而我們在這一步已經(jīng)將Cell的內(nèi)容配置完畢了 - 通過
[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)需要說:
- 為什么在
cellForRowAtIndexPath
里做緩存
最開始我們已經(jīng)談過了、cellForRowAtIndexPath
的調(diào)用在獲取自動布局的高度之前、這樣也能避免重復(fù)取用對應(yīng)位置的Cell。前提是你開啟了預(yù)估行高、具體見下面的解釋。
這里需要補(bǔ)充一下
cellForRowAtIndexPath
與heightForRowAtIndexPath
調(diào)用到底誰先誰后:
- 如果你的tableView設(shè)置了預(yù)估行高
cellForRowAtIndexPath
永遠(yuǎn)在heightForRowAtIndexPath
之前- 如果你的tableView沒有設(shè)置預(yù)估行高
tableView首先會把所有IndexPath的heightForRowAtIndexPath
輪訓(xùn)一遍以計(jì)算contentSize
。而后按照情況1的順序。
所以如果你想不設(shè)置預(yù)估行高、又想用這種方式緩存的話。需要把緩存的代碼寫在heightForRowAtIndexPath
而不是cellForRowAtIndexPath
里
而返回的UITableViewAutomaticDimension
主要是為了容錯(cuò)(比如上面的情況)。
- 為什么用
systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority
網(wǎng)上很多帖子都這樣寫:
[cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]
但是在我這不太好用、因?yàn)槲襝ell內(nèi)部有一些優(yōu)先級的設(shè)置。
所以、我干脆和系統(tǒng)調(diào)用的方式一樣。
- 異步計(jì)算
是的、我們又可以異步計(jì)算了。雖然我沒寫、因?yàn)槲椰F(xiàn)在得抓緊碼頁面~
關(guān)于一些舊帖子
我搜到的都是14/15年的帖子、和現(xiàn)在的情況感覺還是有出入的。
-
cell.contentView
取出的高度要+1
網(wǎng)上對+1的解釋是、cell
比cell.contentView
要搞出1個(gè)單位。還附上了兩張圖。
但是現(xiàn)在、cell是比cell.contentView高出0.5(0.5也不一定準(zhǔn)確、xib上有四舍五入的嫌疑)、而不是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ǔ)充以及不吝賜教小弟會更加感激。