iOS-動態調整UITableViewCell的高度


OS-動態調整UITableViewCell的高度iOS開發文檔, by 友盟翻譯組 stefaliu.

大概你第一眼看來,動態調整高度是一件不容易的事情,而且打算解決它的第一個想法往往是不正確的。在這篇文章中我將展示如何使圖表單元格的高度能根據里面文本內容來動態改變,同時又不必子類化UITableViewCell。你當然可以通過子類化它來實現,但是這樣做會使得代碼復雜因為設置高度是在圖表本身的實例上而不是對單元格操作。下面你將會看到這其實是一件輕而易舉的事情。對於圖表來說能夠動態調整高度是件很有意義的事情,我首先想到的需要這個功能的是當顯示一列長度會變化的文本列表時,如果文本內容較少,它或許能夠適合正常的單元格label,但是如果文本變長,就不得不重新設置單元格大小以便於顯示全部的文本內容。我總結了重新設置單元格大小的主要步驟如下:

1 創建並添加一個UILabel作為單元格cell的子視圖; 2 在UITableView的委托方法: (CGFloat)tableView:(UITableView*)tableViewheightForRowAtIndexPath: (NSIndexPath *) indexPath中計算高度 3 在UITableView的委托方法: (UITableViewCell*)tableView:(UITableView*)tableViewcellForRowAtIndexPath: (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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM