UITableView flexible / dynamic heightForRowAtIndexPath

Case

Usually you should use the cellForRowAtIndexPath delegate cellForRowAtIndexPath to customize your cell. The information given for the cell is important for how the cell will be drawn and what size.

Unfortunately, the delegate method heightForRowAtIndexPath is called before the delegate method cellForRowAtIndexPath , so we canโ€™t just tell the delegate to return the cell height, because at that time it will be 0.

Thus, we need to calculate the size before the cell is drawn in the table. Fortunately, there is a method that does just that, sizeWithFont , which belongs to the NSString class. However, there is a problem to dynamically calculate the correct size in order to know how the elements in the cell will be represented. I will do this in an example:

Imagine a UITableViewCell that contains a label named textLabel . As part of the cellForRowAtIndexPath delegate cellForRowAtIndexPath we put textLabel.numberOfLines = 0 , which basically tells the label that it can have as many lines as it needs to present text for a specific width. The problem arises if we give textLabel text that is larger than the width originally set for textLabel. A second row will appear, but the cell height will not be automatically adjusted, and we will get a messy table view.

As mentioned earlier, we can use sizeWithFont to calculate the height, but it needs to know which font is used, for what width, etc. If for reasons of simplicity we just care about the width, we could hard code that the width will be around 320.0 (this does not take into account the gasket). But what happens if we use UITableViewStyleGrouped instead of a simple width, then it will be around 300.0, and the cell will be corrupted again. Or what happens if we move from portrait to landscape, we have much more space, but it will not be used, since we hardcoded 300.0.

This is the case when at some point you should ask yourself the question of how much you can avoid hard coding.

My own thoughts

You can call the cellForRowAtIndexPath method, which belongs to the UITableView class, to get a cell for a specific section and row. I read a couple of posts that said you donโ€™t want to do this, but I donโ€™t understand this. Yes, I agree that it will already select the cell, but the delegate method heightForRowAtIndexPath is called only for the cells that will be visible, so the cell will be selected anyway. If you use dequeueReusableCellWithIdentifier , the cell will not be selected again in the cellForRowAtIndexPath method, a pointer will be used instead, and the properties will just be configured. Then what is the problem?

Note that the cell is NOT drawn inside the cellForRowAtIndexPath delegate cellForRowAtIndexPath , when the table view cell becomes visible, the script calls the setNeedDisplay method in UITableVieCell, which invokes the drawRect method to draw the cellForRowAtIndexPath , calling the cellForRowAtIndexPath delegate cellForRowAtIndexPath not directly lose performance because it needs to be done twice.

So, by calling the delegate method cellForRowAtIndexPath in the delegate method heightForRowAtIndexPath , we get all the necessary information about the cell to determine its size.

Perhaps you can create your own sizeForCell method that goes through all the parameters, what if the cell is in the style of Value1 or Value2, etc.

Conclusion / Question

This is just a theory that I described in my thoughts, I would like to know if I wrote correctly. Or maybe there is another way to do the same. Please note that I want to be able to make everything as flexible as possible.

+60
iphone uitableview
06 Feb '10 at 12:11
source share
7 answers

Yes, I agree that it will already select the cell, but the delegate method heightForRowAtIndexPath is called only for the cells that will be visible, so the cell will be selected anyway.

This is not true. In the table view, you must call heightForRowAtIndexPath (if implemented) for all rows that are in the table view, not just the ones currently displayed. The reason is that he needs to figure out his overall height in order to display the correct scroll indicators.

+26
Jul 12 '11 at 16:02
source share

I used this:

  • Creating collection objects (an array of size information (dictionary, NSNumber row heights, etc.) based on the collection objects that will be used to represent the table.

  • This is done when we process data from either a local or remote source.

  • I determine the type and size of the font that will be used when I create these collection objects. You can even store UIFont objects or any custom objects used to represent content.

  • These collection objects will be used every time I implement the UITableViewDataSource or UITableViewDelegate protocols for sizing UITableViewCell instances and its subzones, etc.

This way you can avoid subclassing UITableViewCell to get various properties of its contents.

Do not use an absolute value to initialize frames. Use a relative value based on your current orientation and boundaries.

If we rotate it to any orientation, just create a resizing mechanism at runtime. Verify that the autoresizingMask function is installed correctly.

You only need heights, you don't need all the extra stuff in a UITableViewCell to determine the height of the row. You may not even need a width, because, as I said, the width should be relative to the borders of the view.

+8
Feb 06 '10 at 17:41
source share

Here is my approach to solving this

  • I assume in this solution that only one label has a "dynamic" height
  • I also assume that if we do an automatic label size to stretch the height as the cell grows, you need to change the cell height to change
  • I assume nib has an appropriate spacing for where the shortcut will be, and how much space is above and below it.
  • We do not want to change the code every time we change the font or label position in nib

How to update height:

 -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { // We want the UIFont to be the same as what is in the nib, // but we dont want to call tableView dequeue a bunch because its slow. // If we make the font static and only load it once we can reuse it every // time we get into this method static UIFont* dynamicTextFont; static CGRect textFrame; static CGFloat extraHeight; if( !dynamicTextFont ) { DetailCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; dynamicTextFont = cell.resizeLabel.font; CGRect cellFrame = cell.frame; textFrame = cell.resizeLabel.frame; extraHeight = cellFrame.size.height-textFrame.size.height; // The space above and below the growing field } NSString* text = .... // Get this from the some object using indexPath CGSize size = [text sizeWithFont:dynamicTextFont constrainedToSize:CGSizeMake(textFrame.size.width, 200000.f) lineBreakMode:UILineBreakModeWordWrap]; return size.height+extraHeight; } 

Questions:

  • If you are not using a prototype cell, you will need to check if there is a cell and initialize it.
  • Your nib / storyboard should have automatic UILabel authentication, and for multiple lines - 0
+7
Aug 08 2018-12-12T00:
source share

You should take a look at TTTableItemCell.m in a Three20 structure. This follows from a different approach, mainly if each cell class (with some predefined settings, such as font, layout, etc.) implements the general method + tableView: sizeForItem: (or something like that), where it gets the text in the item object. When you browse text for a specific cell, you can also find the corresponding font.

As for the height of the cell: you can check the width of the table and, if necessary, subtract the fields on the UITableViewStyleGrouped and the width of the final row of the pointer and expansion element (which you are looking for in the data store for the data of your cells). When the width of the View table changes, for example. you must call [tableView reloadData] to rotate the interface.

+4
Feb 06 2018-10-06
source share

To answer the question, the original poster asked what โ€œis it normal to call cellForRowAtIndexPath?โ€, It is not. This will give you a cell, but it will not place it inside this index and it will not be reordered (there is no way to return it), so you just lose it. I believe this will be in the autostart pool and will eventually be freed, but you will still create many mailboxes again and again, and it is really very wasteful.

You can make dynamic cell heights, you can even make them very nice, but there is a lot of work for them to make them look seamless, even more if you want to support multiple orientations, etc.

+3
Jul 12 '11 at 15:57
source share

I have an idea about dynamic cell height.

Just create one instance of your custom cell as a member variable of the UITableViewController . In the tableView:heightForRowAtIndexPath: set the contents of the cell and return the height of the cell.

This way, you wonโ€™t create / auto-implement the cell several times as you would if you call cellForRowAtIndexPath inside the heightForRowAtIndexPath method.

UPD:. For convenience, you can also create a static method in your custom cell class that will instantiate a singleton cell to calculate the height, set the contents of the cell, and then return its height.

tableView:heightForRowAtIndexPath: The body of the function will now look like this:

 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return [MyCell cellHeightForContent:yourContent]; } 
+3
Jul 31 2018-12-12T00:
source share

Here is my solution that I used to implement some pretty smooth cells for a chat application.

Up to this point, heightForCellAtIndexPath has always irritated me: because it violates the DRY principle. With this solution, my heightForRowAtIndexPath: costs 1.5 ms per cell, which I could shave up to ~ 1 ms.

Basically, you want each subview inside your cell to implement sizeThatFits: create an off-screen cell that you customize, then query the root view with sizeThatFits: CGSizeMake (tableViewWidth, CGFLOAT_MAX).

There are a few mistakes along the way. Some types of UIKit have expensive setter operations. For example, [UITextView setText] does a great job. The trick here is to subclass, buffer the variable, then override setNeedsDisplay to call - [super setText:] when the view is displayed. Of course, you will have to implement your own TatFits size: using the UIKit extensions.

+1
Aug 21 '12 at 19:12
source share



All Articles