1

I have the following setup: UITableView with custom UITableViewCell implementation. In each cell I have a UILabel and a UIButton. When the table is first displayed, in each of the cells, the UILabel has the number of lines set to 1 and all cells have fixed height (60 px in my case). When the button in the cell is tapped, then the UILabel's number of lines is set to 0 and word wrap is turned on, effectively expanding the table cell.

Now, the issue is that only the implementation of UITableViewCell knows whether the label is expanded or not - and thus what the cell height should be. However the owner file is the table's datasource.

I can't get may head around how to set this up. Any ideas are appreciated.

Update here are extracts from my code

In the custom UITableViewCell implementation:

- (float)requiredHeight
{
    if(isFull)
    {
        CGSize labelSize = [LblTitle.text sizeWithFont: [LblContent font]
                                     constrainedToSize: CGSizeMake(300.0f, 300.0f) 
                                         lineBreakMode: UILineBreakModeTailTruncation];
        return 42.0f + labelSize.height;
    }
    else
    {
        return 60.0f;
    }
}

In owner file (UITableViewDelegate):

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    OOCommentCell *cell = (OOCommentCell*)[tableView cellForRowAtIndexPath:indexPath];
    return [cell requiredHeight];
}

However this results randomly in an infinite loop or in BAD_ACCESS_EXCEPTION.

Aleks G
  • 56,435
  • 29
  • 168
  • 265

2 Answers2

2

OK, after some struggling, here's what I ended up with, which kind of works.

  1. I created a simple class to contain information related to one cell:
@interface CommentInfo : NSObject
{
    int ID;
    NSString *Name;
    NSString *Date;
    NSString *Content;
    BOOL IsFull;
    float Height;
}

@property (readonly) int ID;
@property (readonly) NSString *Name;
@property (readonly) NSString *Date;
@property (readonly) NSString *Content;
@property (readwrite, assign) float Height;
@property (readwrite, assign) BOOL IsFull;

- (id)initWithID:(int)_id withName:(NSString *)_name withDate:(NSString *)_date withText:(NSString *)_text;

@end

Don't worry too much about all the properties - the most important one is the Height.

  1. In my controller (which is also the delegate for the table view), I keep the data as an NSMutableArray of pointers to objects of type CommentInfo.

  2. In cellForRowAtIndexPath I get the corresponding pointer and pass it to the custom cell implementation during construction, where it is stored. I also set self as a delegate to the cell.

  3. In the custom cell implementation, when I need to expand/change the height, I update the Height property in the CommentInfo object and call a method in the delegate to update the display.

  4. When this updateDisplay method is called, I simple do the following:

[CommentsTable beginUpdates];
[CommentsTable endUpdates];
  1. In heightForRowAtIndexPath method, I retrieve the corresponding pointer to CommentInfo and read the Height property. As the pointer is the same between the controller and the cell, any changes to this property will be visible in both classes.

Job done.

Aleks G
  • 56,435
  • 29
  • 168
  • 265
  • I'm using a UIWebView inside a UITableViewCell, so it can't tell the height when the heightForRowAtIndexPath is being called. What I do is I set the cell as a delegate for the UIWebView and in the webViewDidFinishLoad method I call [webView sizeToFit]; webView.hidden = NO; [tableViewController setRowHeight:webView.bounds.size.height forRowId:id]; and in the setRowHeight method of the UITableViewController i call these two methods you provided: [tableView beginUpdates]; [tableView endUpdates]; and it works. – catamphetamine Jan 11 '14 at 20:40
0

Implement the method UITableViewDelegate – tableView:heightForRowAtIndexPath: in your table delegate. If you want to define height on a per-cell basis, you could 'forward' this call to the cell's class by calling your own tableView:cellForRowAtIndexPath: method in tableView:heightForRowAtIndexPath.

Update: Implemented in code. The error was caused by an incorrect method signature in the delegate calling tableView:cellForRowAtIndexPath.

In the custom UITableViewCell implementation:

- (CGFloat)requiredHeight
{
    if(isFull)
    {
        CGSize labelSize = [LblTitle.text sizeWithFont: [LblContent font]
                                     constrainedToSize: CGSizeMake(300.0f, 300.0f) 
                                         lineBreakMode: UILineBreakModeTailTruncation];
        return 42.0f + labelSize.height;
    }
    else
    {
        return 60.0f;
    }
}

In owner file (UITableViewDelegate):

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    OOCommentCell *cell = (OOCommentCell*)[self tableView:tableView cellForRowAtIndexPath:indexPath];
    return [cell requiredHeight];
}
Jacob Jennings
  • 2,796
  • 1
  • 24
  • 26
  • I don't think I'm following your logic. I have defined `heightForRowAtIndexPath`, in it I tried to get the cell at the indexpath and then call a method in it to return the required path, however this results in bad_access_exception. – Aleks G Dec 19 '11 at 22:47
  • Essentially, the table is asking the UITableViewDelegate, "How tall should row X be?" via the method tableView:heightForRowAtIndexPath:. You should return a CGFloat of the desired height in pixels, not an index path. – Jacob Jennings Dec 19 '11 at 23:02
  • @JacobJinnings: you seem to misread my code. To "how tall should row X be", I'm trying to get that row X and ask it how tall it should be - and return that value. Please have a look at the code more carefully. – Aleks G Dec 19 '11 at 23:08