OS-動態(tài)調(diào)整UITableViewCell的高度 iOS開發(fā)文檔 , by 友盟翻譯組 stefaliu.
大概你第一眼看來,動態(tài)調(diào)整高度是一件不容易的事情,而且打算解決它的第一個想法往往是不正確的。在這篇文章中我將展示如何使圖表單元格的高度能根據(jù)里面文本內(nèi)容來動態(tài)改變,同時又不必子類化 UITableViewCell 。你當(dāng)然可以通過子類化它來實現(xiàn),但是這樣做會使得代碼復(fù)雜因為設(shè)置高度是在圖表本身的實例上而不是對單元格操作。下面你將會看到這其實是一件輕而易舉的事情。對于圖表來說能夠動態(tài)調(diào)整高度是件很有意義的事情,我首先想到的需要這個功能的是當(dāng)顯示一列長度會變化的文本列表時,如果文本內(nèi)容較少,它或許能夠適合正常的單元格label,但是如果文本變長,就不得不重新設(shè)置單元格大小以便于顯示全部的文本內(nèi)容。我總結(jié)了重新設(shè)置單元格大小的主要步驟如下:
1 創(chuàng)建并添加一個
UILabel
作為單元格cell的子視圖;
2 在UITableView的委托方法: (CGFloat)
tableView
:(UITableView*)
tableView
heightForRowAtIndexPath
: (NSIndexPath *) indexPath中計算高度
3 在UITableView的委托方法: (
UITableViewCell
*)
tableView
:(UITableView*)
tableView
cellForRowAtIndexPath
: (NSIndexPath *) indexPath中計算
UILabel
的框大小。
下面我要詳細(xì)介紹這些步驟,首先看一下程序輸出截圖:
在普通的圖表中,你可以簡單地用下面的方法設(shè)置單元格內(nèi)label的文本內(nèi)容:
[ [ cell textLabel ] setText : @ "Text for the current cell here." ] ;
也許你認(rèn)為這樣做就可以完全控制 UILabel 了,但是我發(fā)現(xiàn)我的任何要改變 UILabel 框大小的嘗試都失敗了,因此這并不是實現(xiàn)動態(tài)調(diào)整大小的一個好的候選方案。
我們需要設(shè)計一個 UILabel 然后把它添加到單元格的內(nèi)容視圖中。要實現(xiàn)它需要調(diào)用- cellForRowAtIndexPath ,大致內(nèi)容如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
- ( UITableViewCell * ) tableView : ( UITableView * ) tv cellForRowAtIndexPath : ( NSIndexPath * ) indexPath { UITableViewCell * cell; UILabel * label = nil ; ? cell = [ tv dequeueReusableCellWithIdentifier : @ "Cell" ] ; if ( cell == nil ) { cell = [ [ [ UITableViewCell alloc ] initWithFrame : CGRectZero reuseIdentifier : @ "Cell" ] autorelease ] ; ? label = [ [ UILabel alloc ] initWithFrame : CGRectZero ] ; [ label setLineBreakMode : UILineBreakModeWordWrap ] ; [ label setMinimumFontSize : FONT_SIZE ] ; [ label setNumberOfLines : 0 ] ; [ label setFont : [ UIFont systemFontOfSize : FONT_SIZE ] ] ; [ label setTag : 1 ] ; ? [ [ cell contentView ] addSubview : label ] ; } } |
這并不是完整的代碼因為我們僅僅在創(chuàng)建單元格的時候初始化它的label,這段代碼對應(yīng)調(diào)用-dequeueReusableCellWithIdentifier之后的判斷模塊if(cell == nil)。
在這里我想強調(diào)兩點:第一個,我們可以注意到label有一個標(biāo)簽與其對應(yīng),因為調(diào)用了-setTag:1。當(dāng)cell不等于nil時這個標(biāo)簽可以用到。第二點,我們通過調(diào)用[[cell contentView] addSubview:label]來將label添加到單元格的內(nèi)容視圖中,這個只是在label初始化的時候用到。每調(diào)用這個函數(shù)都會添加label 到子視圖序列中。下面我們會將這段代碼補充完整,但之前先讓我們看一下如何設(shè)置cell的高度。
計算cell的高度
在一個復(fù)雜的cell中,計算高度可能比較困難,但是你只需要關(guān)心那些高度會變化的部件就可以了。在我的例子中,唯一需要處理的就是添加到單元格中的label。我們根據(jù)文本的大小來計算cell 的高度,而文本的大小取決于文本的長度和文本字體。NSString類提供了函數(shù)-sizeWithFont來方便我們獲取cell 的大小。下面的代碼介紹了函數(shù)- heightForRowAtIndexPath :
1 2 3 4 5 6 7 8 9 10 11 12 |
- ( CGFloat ) tableView : ( UITableView * ) tableView heightForRowAtIndexPath : ( NSIndexPath * ) indexPath; { NSString * text = [ items objectAtIndex : [ indexPath row ] ] ; ? CGSize constraint = CGSizeMake ( CELL_CONTENT_WIDTH - ( CELL_CONTENT_MARGIN * 2 ) , 20000.0f ) ; ? CGSize size = [ text sizeWithFont : [ UIFont systemFontOfSize : FONT_SIZE ] constrainedToSize : constraint lineBreakMode : UILineBreakModeWordWrap ] ; ? CGFloat height = MAX ( size.height, 44.0f ) ; ? return height + ( CELL_CONTENT_MARGIN * 2 ) ; } |
你會注意到我們用到了幾個常量來計算cell 的大小,它們的定義如下所示:
#define FONT_SIZE 14.0f #define CELL_CONTENT_WIDTH 320.0f #define CELL_CONTENT_MARGIN 10.0f
常量CELL_CONTENT_WIDTH是整個cell的寬度。CELL_CONTENT_MARGIN是我們定義的頁邊空白,F(xiàn)ONT_SIZE是我們采用文本的字體大小。
首先我們要創(chuàng)建一個內(nèi)容寬度的約束條件。CGSizeMake的第一個參量是總共的內(nèi)容寬度減去兩個頁邊空白。因為左邊和右邊各有一個頁邊空白。第二個參數(shù)是我們提供的最大數(shù)值。這個約束條件在后面的函數(shù)-sizeWithFont中將會用到。在-sizeWithFont中我們設(shè)置為 UILineBreakModeWordWrap來獲取在允許自動換行的情況和上面提到的約束條件下正確的大小。最后我們使用MAX宏設(shè)置cell的高度,并且保證cell 的高度不會小于44個像素,因為它返回size. height 和44兩個數(shù)中的最大值。最后,我們將上下的頁邊空白考慮進(jìn)去得到最后的結(jié)果。
為了使得讀者形象化的了解頁邊空白,下面一個截圖可以看出有一個邊界環(huán)繞著label。調(diào)用[[label layer] setBorderWidth:2.0f]可以顯示該邊界從而方便我們看到頁邊空白。
計算并設(shè)置
UILabel
框大小
在前面我們用來計算高度的方法也是我們用來設(shè)置 UILabel 框大小的方法。下面將- cellForRowAtIndexPath 代碼補充完整:
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 28 29 30 31 32 33 34 35 36 |
- ( UITableViewCell * ) tableView : ( UITableView * ) tv cellForRowAtIndexPath : ( NSIndexPath * ) indexPath { UITableViewCell * cell; UILabel * label = nil ; ? cell = [ tv dequeueReusableCellWithIdentifier : @ "Cell" ] ; if ( cell == nil ) { cell = [ [ [ UITableViewCell alloc ] initWithFrame : CGRectZero reuseIdentifier : @ "Cell" ] autorelease ] ; ? label = [ [ UILabel alloc ] initWithFrame : CGRectZero ] ; [ label setLineBreakMode : UILineBreakModeWordWrap ] ; [ label setMinimumFontSize : FONT_SIZE ] ; [ label setNumberOfLines : 0 ] ; [ label setFont : [ UIFont systemFontOfSize : FONT_SIZE ] ] ; [ label setTag : 1 ] ; ? [ [ label layer ] setBorderWidth : 2.0f ] ; ? [ [ cell contentView ] addSubview : label ] ; ? } NSString * text = [ items objectAtIndex : [ indexPath row ] ] ; ? CGSize constraint = CGSizeMake ( CELL_CONTENT_WIDTH - ( CELL_CONTENT_MARGIN * 2 ) , 20000.0f ) ; ? CGSize size = [ text sizeWithFont : [ UIFont systemFontOfSize : FONT_SIZE ] constrainedToSize : constraint lineBreakMode : UILineBreakModeWordWrap ] ; ? if ( ! label ) label = ( UILabel * ) [ cell viewWithTag : 1 ] ; ? [ label setText : text ] ; [ label setFrame : CGRectMake ( CELL_CONTENT_MARGIN, CELL_CONTENT_MARGIN, CELL_CONTENT_WIDTH - ( CELL_CONTENT_MARGIN * 2 ) , MAX ( size.height, 44.0f ) ) ] ; ? return cell; } |
要注意if(cell == nil)模塊是初始化代碼,只在cell創(chuàng)建的時候運行一次。該模塊外部代碼每次都會執(zhí)行只要在每次數(shù)據(jù)更新或者窗口拖拽之后調(diào)用了- cellForRowAtIndexPath 。
也就是說,每次都需要設(shè)置label中文本內(nèi)容以及設(shè)置label外框大小。注意如果label處于未初始化狀態(tài),我們需要通過調(diào)用[cell viewWithTag:1]來獲取 UILabel 的句柄。這段代碼跟前面計算高度的代碼基本相同。
總結(jié)
動態(tài)計算單元格cell的高度真的并不困難。如果你有一個很復(fù)雜的cell,你只需要根據(jù)內(nèi)容寬度和特定文本字體的大小來確定cell的高度。如果你不清楚你的外框顯示在什么地方,只需要通過調(diào)用[[view layer] setBorderWidth:2.0f]來使外框顯示即可。這會有助于你了解繪圖過程以及更快地在更深的層次理解繪圖顯示的問題。
演示工程文件: DynamicHeights Demo Project
作者:Matt Long
原文鏈接:
http://www.cimgf.com/2009/09/23/uitableviewcell-dynamic-height/
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
