iOS学习之路十三(动态调整UITableViewCell的高度)
大概你第一眼看來,動態調整高度是一件不容易的事情,而且打算解決它的第一個想法往往是不正確的。在這篇文章中我將展示如何使圖表單元格的高度能根據里面文本內容來動態改變,同時又不必子類化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,大致內容如下所示:
?
[html]?view plaincopyprint?- -?(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:
[html]?view plaincopyprint?- -?(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代碼補充完整:
[html]?view plaincopyprint?- -?(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/
原文:
UITableViewCell Dynamic Height
At first glance setting a height dynamically for table view cells seems a little daunting and the first most obvious answers that come to mind are not necessarily correct. In this post I will show you how to set your table view cell heights dynamically based upon the text content without subclassing UITableViewCell. You can subclass it, however, doing so does not make the code much cleaner as setting the height is done in your delegate for the table view itself rather than the cell anyhow. Read on to see what you need to know to make dynamic cell height sizing a breeze. There are probably numerous reasons why you might want dynamic heights for your table view cells, but the one I’ve run into most is the need to resize because I am displaying lists of text objects with varying lengths. When the text is short, it might fit in the normal cell label, however, if the text gets longer, you will want to resize the cell so that you can display the complete content. I’ve distilled the process of resizing table cells to a few rules of thumb. Here they are:
- Create, configure, and add a UILabel as a subview of the contentView in the cell.
- Calculate the height in the UITableView delegate method, – (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
- Calculate the frame for the UILabel in the UITableView delegate method, – (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
I am going to cover each of these rules in detail, but take a look at the output of the example project in the screenshot.
In simpler table view based applications, you can simply set the text of the table view cell’s text label like this:
[[cell textLabel] setText:@"Text for the current cell here."]; Doing so might make you think that you can manipulate the UILabel that the cell uses, however, I’ve found my attempts to change the UILabel’s frame get ignored completely, so it is not a good candidate for use with our dynamic resizing code.
Instead what we need to do is programatically create a UILabel and add it to the cell’s content view. Do this in the call to -cellForRowAtIndexPath. Use something like the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
This is not the completed code as you’ll notice that we have initialized the label only when the cell needs created for the first time, that is if (cell == nil) after a call to -dequeueReusableCellWithIdentifier. There are two points I want to address in relation to this. First, notice the label has a tag associated with it after a call to -setTag:1. This will be used in the case where the cell is not equal to nil after a call to -dequeueReusableCellWithIdentifier. In that case we will need to get a handle to the label by calling [cell viewWithTag:1] which will return the view that we associated with that tag. Second, notice that we have added our label to the cell’s content view with a call to [[cell contentView] addSubview:label]. This is done when the label is initialized and should only be done once. Adding it each time this method is called will add the label again to the subviews array. We will come back to this code to finish it in a minute, but first let’s take a look at how we can set the height for our cell now that our label has been added.
Calculate the Cell Height
In a complex cell, your calculations could get a bit challenging, however, you only need to worry with the items that will change in height. In our example, the only item you need to deal with is the label that we added. We calculate the height of the cell by determining the size of the text based on the length of the text and the font we intend to use. The NSString class provides a method called -sizeWithFont that enables us to obtain this size. The following code show how we implement our call to -heightForRowAtIndexPath:
1 2 3 4 5 6 7 8 9 10 11 12 | |
You will notice that we have several constants we are using to calculate the size of our cell. These are defined as follows:
#define FONT_SIZE 14.0f
#define CELL_CONTENT_WIDTH 320.0f
#define CELL_CONTENT_MARGIN 10.0f The constant CELL_CONTENT_WIDTH is the width of the entire cell. CELL_CONTENT_MARGIN is the margin we want to use all the way around the cell as the content inset, and of course FONT_SIZE is the size of the font we want to use for the label text.
The first place we use these is to create a constraint with the content width. Notice that CGSizeMake takes as its first parameter the total content width minus the margin times 2. This subtracts the margin from the left and the margin from the right from the total width to have the actual width of the label. The second parameter is just a maximum number we provide. The call to -sizeWithFont will set this to the actual height in the next line. This call to -sizeWithFont calculates the size according to the constant UILineBreakModeWordWrap which causes it to return the correct size for word wrap–which is why the width is important to get right. Next we set our height for the cell using a call to the MAX macro. This will ensure that our cell height will never be shorter than the default 44 pixels as MAX returns the larger of the two variables. Finally, we add our margin height back into the height for both top and bottom (hence x 2) and then return the result.
To help visualize how the margin is working, take a look at the following screenshot to see what each label looks like with a border around it. Turning this on with a call to [[label layer] setBorderWidth:2.0f] on the UILabel we added in the previous section makes it clear where the margins are.
Calculate the UILabel Frame and Set It
The same calculation we used to determine the height in the previous section is the code we use to set the frame for the UILabel we added in the beginning. To complete this tutorial we will finish out our implementation of -cellForRowAtIndexPath with the following code.
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 | |
Just remember that anything done within the if (cell == nil) block is initialization code and should only be done when the cell is first created. Anything done outside of the block will be used every time the -cellForRowAtIndexPath is called, which is any time the data gets reloaded or the view gets scrolled.
That being said, you will see that the only thing we do every time it gets called is setting the text of the current item and setting the label’s frame for the current item (lines 32 and 33). Notice that we got a handle to our UILabel by calling [cell viewWithTag:1] (lines 29 and 30) in the case where the label is nil in subsequent/non-initialization calls to this method. You will notice that our frame calculation code is exactly the same as what we used in the previous section to determine the row height.
Conclusion
Calculating dynamic cell heights is really not too hard. If you have a very complex cell, just remember that all you really need to calculate is the height based up a width that shouldn’t change and the size of the text of a certain font (unless of course you support both portrait and landscape modes–which makes things a little more challenging. I will, however, leave this as an exercise for the reader). If you find yourself wondering where your actual frame is displaying for a given view, just turn on the view border by calling [[view layer] setBorderWidth:2.0f]. This will help you see what is going on and give you the ability get to the bottom of your display problems quicker. Until next time.
DynamicHeights Demo Project
Tags: ?
cellForRowAtIndexPathdynamicheightheightForRowAtIndexPathiOStableviewUILabelUITableViewCell轉載于:https://www.cnblogs.com/lixingle/p/3308067.html
總結
以上是生活随笔為你收集整理的iOS学习之路十三(动态调整UITableViewCell的高度)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 唱到喉咙沙哑是什么歌?
- 下一篇: Earth to developers: