8
votes

I'm having some problems implemented dynamic row heights in a UITableView - but it isn't the cells that I'm having a problem with, its the UILabel inside of the cell.

The cell just contains a UILabel to display text. My tableView:heightForRowAtIndexPath: is correctly resizing each cell by calculating the height of the label that will be in it using NSString's sizeWithFont: method.

I have a subclass of UITableViewCell that just holds the UILabel property that is hooked up in storyboard. In storyboard I've set its lines to 0 so it will use as many lines as it needs, and I've set its lineBreak to Word Wrap.

Here is how I'm setting up the cells:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath  *)indexPath
{
    ExpandCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"  forIndexPath:indexPath];

   SomeObject *object = self.tableObjects[index.row];

    cell.myLabel.text = [object cellText];

    [cell.myLabel sizeToFit];

    return cell;
}

When I build this, I get my table view with the cell's all sized to the correct height for their content, but the labels are all 1 line that just runs off the side of the cells. However, if I scroll the table so cell's leave the screen, and then scroll back to them, their label will be resized correctly and the cell will look how I expected it to initially.

I have also attempted calculating the labels frame with the same method I'm calculating the row height with, and I get the same behavior - it doesn't draw correctly until it scrolls off of the screen and back on again.

I have found two ways to work around this, and neither are acceptable solutions.

First, if in viewDidAppear: I call reloadData on my tableview, the cells and labels draw themselves correctly the first time. This won't work for my situation because I will be adding and removing cells to this table, and I don't want to call reloadData every time a cell is added.

The second workaround seems very strange to me - if I leave the font settings at the default System Font 17 on the UILabel, the cells draw themselves correctly. As soon as I change the font size, it reverts to its behavior of not drawing a label correctly until it leaves the screen and comes back, or gets reloadData called on the tableView.

I'd appreciate any help with this one.

3
This post correctly answers your problem: stackoverflow.com/questions/8903369/…. It can be a little tricky if you've never created dynamically sized cells before, but isn't too difficult. - macandyp
I don't understand how the code in that answer is doing anything different than what I'm doing. I AM setting my label's frame in cellForRowAtIndexPath, but it isn't applying that frame until the next time it is redrawn. Or am I missing something? - alivingston
Are you implementing heightForRowAtIndexPath? - macandyp
Yes. The cell height is being calculated correctly every time. It's the label inside of the cell that doesn't get the correct frame until its redrawn. - alivingston
Can you use ios6 NSConstraints? Label trailing and leading space to its superview cell can be kept to a constant. So if cell resizes, the label will resize. - sridevi

3 Answers

8
votes

I ended up resolving this by alloc/init'ing the label in cellForRowAtIndexPath. I'm not entirely sure why this is a solution - but it appears the problem I was experiencing has to do with how storyboard (or when, perhaps?) creates the objects within the cell. If I alloc/init the label in the cell in cellForRowAtIndexPath, everything loads and sizes correctly.

So... my current fix is to check if the cell has my custom label in it. If it doesn't, I alloc/init the label and put it in the cell. If it does have one, as in its a cell that's been dequeued, then I just set the text in the label that is already there.

Not sure if its the best solution, but its working for now.

3
votes

I ended up resolving this by unchecking the AutoSizing checkbox in IB. It is unclear why auto-layout was causing this problem.

3
votes

I ran over the same problem and I end up solving it by calling [cell layoutIfNeeded] before return the cell

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath  *)indexPath {
ExpandCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"  forIndexPath:indexPath];
SomeObject *object = self.tableObjects[index.row];

cell.myLabel.text = [object cellText];

[cell layoutIfNeeded];

return cell; }