OS-動態調整UITableViewCell的高度 iOS開發文檔 , by 友盟翻譯組 stefaliu.
大概你第一眼看來,動態調整高度是一件不容易的事情,而且打算解決它的第一個想法往往是不正確的。在這篇文章中我將展示如何使圖表單元格的高度能根據里面文本內容來動態改變,同時又不必子類化 UITableViewCell 。你當然可以通過子類化它來實現,但是這樣做會使得代碼復雜因為設置高度是在圖表本身的實例上而不是對單元格操作。下面你將會看到這其實是一件輕而易舉的事情。對于圖表來說能夠動態調整高度是件很有意義的事情,我首先想到的需要這個功能的是當顯示一列長度會變化的文本列表時,如果文本內容較少,它或許能夠適合正常的單元格label,但是如果文本變長,就不得不重新設置單元格大小以便于顯示全部的文本內容。我總結了重新設置單元格大小的主要步驟如下:
1 創建并添加一個
UILabel
作為單元格cell的子視圖;
2 在UITableView的委托方法: (CGFloat)
tableView
:(UITableView*)
tableView
heightForRowAtIndexPath
: (NSIndexPath *) indexPath中計算高度
3 在UITableView的委托方法: (
UITableViewCell
*)
tableView
:(UITableView*)
tableView
cellForRowAtIndexPath
: (NSIndexPath *) indexPath中計算
UILabel
的框大小。
下面我要詳細介紹這些步驟,首先看一下程序輸出截圖:
在普通的圖表中,你可以簡單地用下面的方法設置單元格內label的文本內容:
[
[
cell textLabel
]
setText
:
@
"Text for the current cell here."
]
;
也許你認為這樣做就可以完全控制 UILabel 了,但是我發現我的任何要改變 UILabel 框大小的嘗試都失敗了,因此這并不是實現動態調整大小的一個好的候選方案。
我們需要設計一個 UILabel 然后把它添加到單元格的內容視圖中。要實現它需要調用- cellForRowAtIndexPath ,大致內容如下所示:
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
]
;
}
}
|
這并不是完整的代碼因為我們僅僅在創建單元格的時候初始化它的label,這段代碼對應調用-dequeueReusableCellWithIdentifier之后的判斷模塊if(cell == nil)。
在這里我想強調兩點:第一個,我們可以注意到label有一個標簽與其對應,因為調用了-setTag:1。當cell不等于nil時這個標簽可以用到。第二點,我們通過調用[[cell contentView] addSubview:label]來將label添加到單元格的內容視圖中,這個只是在label初始化的時候用到。每調用這個函數都會添加label 到子視圖序列中。下面我們會將這段代碼補充完整,但之前先讓我們看一下如何設置cell的高度。
計算cell的高度
在一個復雜的cell中,計算高度可能比較困難,但是你只需要關心那些高度會變化的部件就可以了。在我的例子中,唯一需要處理的就是添加到單元格中的label。我們根據文本的大小來計算cell 的高度,而文本的大小取決于文本的長度和文本字體。NSString類提供了函數-sizeWithFont來方便我們獲取cell 的大小。下面的代碼介紹了函數- 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是我們定義的頁邊空白,FONT_SIZE是我們采用文本的字體大小。
首先我們要創建一個內容寬度的約束條件。CGSizeMake的第一個參量是總共的內容寬度減去兩個頁邊空白。因為左邊和右邊各有一個頁邊空白。第二個參數是我們提供的最大數值。這個約束條件在后面的函數-sizeWithFont中將會用到。在-sizeWithFont中我們設置為 UILineBreakModeWordWrap來獲取在允許自動換行的情況和上面提到的約束條件下正確的大小。最后我們使用MAX宏設置cell的高度,并且保證cell 的高度不會小于44個像素,因為它返回size. height 和44兩個數中的最大值。最后,我們將上下的頁邊空白考慮進去得到最后的結果。
為了使得讀者形象化的了解頁邊空白,下面一個截圖可以看出有一個邊界環繞著label。調用[[label layer] setBorderWidth:2.0f]可以顯示該邊界從而方便我們看到頁邊空白。
計算并設置
UILabel
框大小
在前面我們用來計算高度的方法也是我們用來設置 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創建的時候運行一次。該模塊外部代碼每次都會執行只要在每次數據更新或者窗口拖拽之后調用了- cellForRowAtIndexPath 。
也就是說,每次都需要設置label中文本內容以及設置label外框大小。注意如果label處于未初始化狀態,我們需要通過調用[cell viewWithTag:1]來獲取 UILabel 的句柄。這段代碼跟前面計算高度的代碼基本相同。
總結
動態計算單元格cell的高度真的并不困難。如果你有一個很復雜的cell,你只需要根據內容寬度和特定文本字體的大小來確定cell的高度。如果你不清楚你的外框顯示在什么地方,只需要通過調用[[view layer] setBorderWidth:2.0f]來使外框顯示即可。這會有助于你了解繪圖過程以及更快地在更深的層次理解繪圖顯示的問題。
演示工程文件: DynamicHeights Demo Project
作者:Matt Long
原文鏈接:
http://www.cimgf.com/2009/09/23/uitableviewcell-dynamic-height/
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

