iOS-動態調整UITableViewCell的高度,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,大致內容如下所示:
1234567891011121314151617181920 |
- (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:
123456789101112 |
- (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代碼補充完整:
123456789101112131415161718192021222324252627282930313233343536 |
- (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/