UITableViewCell的估算機制與高度計算

Intro

UITableView 是個UIScrollView,而UIScrollView需要知道contenSize才能根據(jù)bounds,contentInset,contentOffset共同決定是否可以滑動以及滾動條的長度和位置.因此,UITableView需要知道自己需要創(chuàng)建的cell的個數(shù)和每個cell的高度,才能得到最終的contentSize.

iOS7

估算機制

在iOS7及iOS7以下的系統(tǒng)中,當tableView要展示時,會調(diào)用代理的數(shù)據(jù)源方法- tableView: heightForRowAtIndexPath:(tableView獲取高度默認訪問rowHeight屬性,實現(xiàn)了這個方法后,rowHeight屬性就會失效,所以對于等高的cell來說, rowHeight能夠減少許多不必要的計算和方法調(diào)用的開銷)獲得所有row的高度以確定scrollViewcontentSize.

  • - tableView: heightForRowAtIndexPath:適用于不等高cell(沒實現(xiàn)該數(shù)據(jù)源方法就使用rowHeight屬性值)
  • rowHeight適用于等高cell (rowHeight在iOS7中默認為44.f,iOS8為-1.f,即UITableViewAutomaticDimension)

以上兩種用于計算cell的高度的方法,都是在tableView將要顯示的時候,一次性計算出所有cell.這個時候,如果所展示的cell很多,并且每個cell的高度也很高,那么耗費在計算高度的初始時間將會多很多,所以蘋果在iOS7的時候推出了estimatedRowHeight這個屬性(與之類似的還有estimatedSectionHeaderHeight,estimatedSectionFooterHeight).

顧名思義,這個屬性的意義在于估算.有了這個估算高度,就能確定contenSize啦,但是這個contenSize是個估算值,是通過estimatedRowHeightxcell的個數(shù)得到初始的contenSize中的高度,并不是最終的contenSize.實現(xiàn)了這個屬性后,tableView就不會一次性計算所有的cell的高度了,只會計算當前屏幕能夠顯示的cell個數(shù)加上幾個.滑動時,tableView不停地得到新的cell,調(diào)用數(shù)據(jù)源方法得到高度,更新自己的contenSize,在滑到最后的時候,會得到正確的contenSize,在這過程中,旁邊的滾動條會不可避免地"抖動",因為contenSize在不停地更新.因此,在設(shè)置estimatedRowHeight時要給出盡量接近cell高度平均值的數(shù)值,讓"抖動"更小.

// 從打印能看出,contentSize會按照一定的算法進行更新

self.tableView.estimatedRowHeight = 100;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    NSLog(@"%@",NSStringFromCGSize(tableView.contentSize));
    return 300;
}

此外,iOS7中每個cell的高度會被系統(tǒng)自動緩存起來,不會再重復(fù)計算了.

高度計算

在iOS7上,設(shè)置一個cell的尺寸有兩種方式:

  1. AutoLayout
  2. Manual-sizing code (即人工手算)

第二種方式比較常見,通常我們通過在- tableView:cellForRowAtIndexPath代理方法中將模型賦值給cell,cell通過模型計算高度,并將高度的計算結(jié)果存儲在模型身上,然后在tableView: heightForRowAtIndexPath:方法中用cell模型中的高度屬性獲得高度.

其實第一種方式也很方便,最需要注意的是要設(shè)置好完整的約束.系統(tǒng)提供給了我們一個API叫- systemLayoutSizeFittingSize:,可以通過約束計算到cell的高度.

// 設(shè)置一個屬性,或者是靜態(tài)的全局變量的cell.因為這個cell是為了計算高度而生,不必每次調(diào)用方法都創(chuàng)建.
@property (nonatomic, strong) XXXTableViewCell *prototypeCell; 

// 初始化時
self.prototypeCell  = (XXXTableViewCell *)[self.tableView dequeueReusableCellWithIdentifier:@"XXXTableViewCell"];

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 
  // 你或許會想到調(diào)用- tableView:cellForRowAtIndexPath方法拿到當前indexPath的cell,然后通過systemLayoutSizeFittingSize方法來計算高度.可是顯示tableView之前只有拿到高度才能創(chuàng)建cell實例.如果在這里面調(diào)用該方法,會死循環(huán).
    XXXTableViewCell *cell = self.prototypeCell; 
    cell.model = [self.modelArray objectAtIndex:indexPath.row]; 
    CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
  // 為什么要加上一個奇怪的數(shù)呢?  因為這里計算的只是cell的contentView的高度,分割線還占1個像素的高度.
    return 1.0 / [UIScreen mainScreen].scale  + size.height; 
} 

至于1個像素為什么是這樣子計算,請看我的另一篇文章:<iOS中線寬與像素的關(guān)系>. 不那么嚴謹?shù)脑?給個1也沒什么大問題.

另外需要注意的是,在iOS7下(iOS8沒有這個問題),如果布局中有UILabel,并且行數(shù)大于0時,需要指定preferredMaxLayoutWidth,這樣Label才能知道自己什么時候該換行,然后- systemLayoutSizeFittingSize才能得到正確的高度.另一種方案就是,給cell的contentView添加一個和tableView寬度相同的寬度約束,這樣就能在UILabel約束完備的情況下算出UILabel的寬度.(因為contentView寬度的計算需要知道子控件寬度的累加,而UILabel的換行卻依賴著contentView的寬度,不換行就不知道UILabel的高度!)

iOS8

高度計算與iOS7不同的地方

iOS8的變化不小,提出了self-sizing cell的概念,認為cell是可以隨時被改變高度的.self-sizing cell對于支持Dynamic Type非常有用厘熟。你可能沒聽說過Dynamic Type旗扑,但你可能見過系統(tǒng)的“Settings”屏幕:

"Dynamic Type最初由iOS 7引入,允許用戶自定義文本大小從而滿足app的需要晶默。不過僅有采用Dynamic Type的app才能響應(yīng)文本的改變读存,可能只有一小部分第三方應(yīng)用使用了該功能为流。

從iOS 8開始窜醉,蘋果想要鼓勵開發(fā)者使用Dynamic Type。正如在WWDC session中提到的那樣艺谆,所有蘋果系統(tǒng)級應(yīng)用都使用了Dynamic Type榨惰,并且內(nèi)置的標簽已經(jīng)有了動態(tài)字體。當用戶改變文本大小時静汤,這些標簽也會改變其大小琅催。

更進一步說,Self Sizing Cell的引入是促進Dynamic Type使用的辦法虫给,它可以節(jié)省大量寫代碼調(diào)整行高的時間藤抡。如果單元格可以自動調(diào)整了,那么使用Dynamic Type就很顯而易見了抹估。

你只需要從尺寸固定的自定義字體中將字體更改為文本類型(比如標題和內(nèi)容主體)首選的字體缠黍。也就是說當你運行app時,它會適應(yīng)文本大小的改變药蜻。"

對于tableView來說,self-sizing cell帶來的變化就是cell的高度不再被緩存起來了.在iOS7中,當tableView滑到最底下時,所有的cell的高度都被經(jīng)過計算,之后不管怎么滑,都不會進行額外的計算了.而iOS8,不管怎樣都會重新計算cell的高度,因此對于性能的影響較大.如果對程序性能要求較高,就需要設(shè)計一套緩存機制,將每個cell的高度緩存起來.

自動算高

另外,self-sizing cell的另外一個重要功能就是實現(xiàn)了自動算高,不過需要滿足2個條件:

  1. 使用Autolayout進行布局
  2. 設(shè)置estimatedRowHeight的值

然后就OK了.

參考文章

http://blog.sunnyxx.com/2015/05/17/cell-height-calculation/
https://github.com/xhzengAIB/iOS8SelfSizingCells

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瓷式,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子语泽,更是在濱河造成了極大的恐慌贸典,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件踱卵,死亡現(xiàn)場離奇詭異廊驼,居然都是意外死亡,警方通過查閱死者的電腦和手機惋砂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門妒挎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人西饵,你說我怎么就攤上這事酝掩。” “怎么了罗标?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵庸队,是天一觀的道長积蜻。 經(jīng)常有香客問我闯割,道長,這世上最難降的妖魔是什么竿拆? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任宙拉,我火速辦了婚禮,結(jié)果婚禮上丙笋,老公的妹妹穿的比我還像新娘谢澈。我一直安慰自己煌贴,他們只是感情好,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布锥忿。 她就那樣靜靜地躺著牛郑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪敬鬓。 梳的紋絲不亂的頭發(fā)上淹朋,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天,我揣著相機與錄音钉答,去河邊找鬼础芍。 笑死,一個胖子當著我的面吹牛数尿,可吹牛的內(nèi)容都是我干的仑性。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼右蹦,長吁一口氣:“原來是場噩夢啊……” “哼诊杆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起何陆,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤刽辙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后甲献,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宰缤,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年晃洒,在試婚紗的時候發(fā)現(xiàn)自己被綠了慨灭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡球及,死狀恐怖氧骤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吃引,我是刑警寧澤筹陵,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站镊尺,受9級特大地震影響朦佩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜庐氮,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一语稠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦仙畦、人聲如沸输涕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽莱坎。三九已至,卻和暖如春寸士,著一層夾襖步出監(jiān)牢的瞬間型奥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工碉京, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留厢汹,地道東北人。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓谐宙,卻偏偏與公主長得像烫葬,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子凡蜻,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容