7 Replies Latest reply: Jul 9, 2009 10:09 PM by Shaheer
mmalone84 Level 1 Level 1 (20 points)

I'm working on an iPhone app that needs to display variable length text content in UITableView cells. Since the text can run to any number of lines, each row can have a different height. I've read through the UITableView/UITableViewDelegate/UITableViewCell documentation, and looked at the various examples from the dev center. The solution I've come up with uses the tableView:heightForRowAtIndexPath: method to return the calculated row height for each row.

My problem is that I can't come up with an elegant way to calculate the row height. Right now I'm pulling the model object for the row in question and using the NSString:sizeWithFont:constrainedToSize:lineBreakMode method to get a CGSize object, then returning the height. But I'm duplicating a lot of logic that is in the UITableCell object's layoutSubviews method.

I tried calling cellForRowAtPath from heightForRowAtIndexPath, then returning cell.bounds.size.height, but that doesn't work (seems to cause infinite recursion or something... not sure why). Has anyone had this problem? Is there a "best practice" sort of solution? I'm beginning to wonder if I shouldn't just dump everything into a UIWebView and mark stuff up in HTML...

MacBook Pro, Mac OS X (10.5.2)
  • Jason Olson3 Level 1 Level 1 (0 points)
    The problem is the call for cell height is made BEFORE any cell drawing, or layoutSubviews for the table cell is called.

    The best thing to do is figure out the text height you will need and pass that into your custom table cell for drawing.
  • ghostrocket Level 1 Level 1 (0 points)
    Hi -

    I'm looking for a code snippet that demonstrates how to calculate the row height from text. Does anyone have an example?
  • utnow Level 1 Level 1 (0 points)
    I don't know if I can provide a code-snippit for you... it's a fairly complex problem... but I think I should be able to point you in the right direction. I struggled with this for a long time and was finally able to come up with this, and it seems to work pretty well.

    1) Create a custom UITableViewCell class.
    2) Populate you cell however you see fit. I use a 'hydrateWithArray' type function myself.
    3) Once the data has been populated, resize and reposition elements using 'sizeToFit' functions to force labels to conform to the size of whatever you put into them. protip: by setting the frame of the label first, and setting the numer of lines to 0... when you fill the text and sizetofit, it will stretch the label vertically only and force the width to stay the same.
    4) Create a seperate function (mine is called calculatedHeight) that returns a float and returns the height that you would like the cell to be in the table (based on the repositioned objects from step 3).

    - (float)calculatedHeight {
    return textLabel.frame.origin.ytextLabel.frame.size.height5;

    5) In your UITableView class, you'll need to import your tableViewCell class and create a dummy cell object. You're going to use this class to calculate how tall each cell needs to be. Then in the heightOfRowAtIndex method....

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    float height;
    if ( !dummyCell ) dummyCell = [[CustomCell alloc] initWithFrame:CGRectMake(0,0,0,0) reuseIdentifier:@"myCell"];
    [dummyCell hydrateWithTweet:[tableArray objectAtIndex:indexPath.row]];
    height = [dummyCell calculatedHeight];
    if ( height == 0 ) height = 50;
    return height;

    This is a pretty simple example so you may need to go crazy with the error checking in your particular use, but this should at least point you in the right direction. Enjoy!
  • timstephenson Level 1 Level 1 (0 points)
    Here's a sample that may help. My blog post includes a sample project that you can download. The key is using this method to find the height of the string:

    sizeWithFont:<#(UIFont *)font#> constrainedToSize:<#(CGSize)size#> lineBreakMode:<#(UILineBreakMode)lineBreakMode#>

    http://www.raddonline.com/blogs/geek-journal/iphone-sdk-resizing-a-uitableviewce ll-to-hold-variable-amounts-of-text/
  • Laurie Davis Level 1 Level 1 (10 points)
    The following will work. It assumes that the text in the row is in a label.

    The string to display is in a variable called string.
    You need to specify the font (the example is bold, 17pt).
    You need to specify the label width (the example is 200 pixels).
    The height constraint is set to something arbitrarily large (the example is 1000 pixels).
    A one row label is 20 pixels high, therefore 24.0 is added to get a row height of 44.0. This number would have to be adjusted if you are using a different font size.

    - (CGFloat) tableView: (UITableView *) tableView heightForRowAtIndexPath: (NSIndexPath *) indexPath
    CGSize labelSize = CGSizeMake(200.0, 20.0);
    if ([string length] > 0)
    labelSize = [string sizeWithFont: [UIFont boldSystemFontOfSize: 17.0] constrainedToSize: CGSizeMake(labelSize.width, 1000) lineBreakMode: UILineBreakModeWordWrap];
    return 24.0 + labelSize.height;
  • sponno Level 1 Level 1 (0 points)

    I had this issue as well with infinite recursion. To get the current cell height use.

    TweetCell *cell = (TweetCell*)[self tableView:tableView cellForRowAtIndexPath:indexPath];

    Other methods of getting the cell would fail. In my code below I have a few public property eg. textView which is a UITextView.

    - (CGFloat) tableView: (UITableView *) tableView heightForRowAtIndexPath: (NSIndexPath *) indexPath
    // Use the method that you would have created above to get the cell.
    // Any other method seems to result in infinite recursion like you said.
    TweetCell *cell = (TweetCell*)[self tableView:tableView cellForRowAtIndexPath:indexPath];

    NSString *text = cell.textView.text; //[[menuList objectAtIndex:indexPath.row] objectForKey:@"status"];
    CGFloat height = [text sizeWithFont:cell.textView.font constrainedToSize:CGSizeMake(240,70) lineBreakMode:UILineBreakModeWordWrap].height;
    //return MAX(height, MinHeight);

    return height+30;

    Message was edited by: sponno
  • Shaheer Level 1 Level 1 (0 points)
    sponno's method is simply the trick i was able to get to the issue solving by using that option...

    Thanks Man//