Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Thanks, this was very helpful. I didn't want to hard-code in any widths, though. I spent a good while looking at this problem and here's what I found.</p> <p>Basically, there's a chicken-and-egg problem, in that the table view needs to know the heights of the cells to do its own layout, but the cells need to be configured for the geometry of the containing table view before their heights can be determined.</p> <p>One way to break this circular dependency is to hard-code the width to be 320 or 480, which is suggested elsewhere on this page. I didn't like the idea of hard-coding sizes, though.</p> <p>Another idea is to calculate all the heights up-front using a prototype cell. I tried this, but still ran into miscalculated heights because creating a <code>UITableViewCell</code> only leaves you with a cell that's not configured for the actual style and size of the table view. Apparently there's no way manually to configure a cell for a specific table view; you have to let <code>UITableView</code> do it.</p> <p><strong>The strategy I settled upon is to let the table view do everything as it would normally, which includes creating and configuring its own cells, <em>then</em> once all the cells are set up, calculate the actual row heights and refresh the table view.</strong> The downside is that the table view is loaded twice (once with default heights and again with the proper heights), but the upside is that this should be a fairly clean and robust solution.</p> <hr> <p>This sample assumes that you have an instance variable <code>_cellHeights</code> which is an array of arrays that hold the calculated row heights per table view section:</p> <pre><code>@interface MyViewController () { NSArray *_cellHeights; // array of arrays (for each table view section) of numbers (row heights) } </code></pre> <p>Write a method to calculate all the cell heights based on the current cell contents:</p> <pre><code>- (void)_recalculateCellHeights { NSMutableArray *cellHeights = [[NSMutableArray alloc] init]; for (NSUInteger section=0; section&lt;[self.tableView numberOfSections]; section++) { NSMutableArray *sectionCellHeights = [[NSMutableArray alloc] init]; for (NSUInteger row=0; row&lt;[self.tableView numberOfRowsInSection:section]; row++) { NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath]; // Set cell's width to match table view's width CGRect cellFrame = cell.frame; cellFrame.size.width = CGRectGetWidth(self.tableView.frame); cell.frame = cellFrame; CGFloat cellHeight = self.tableView.rowHeight; // default if (cell.textLabel.numberOfLines == 0 || (cell.detailTextLabel &amp;&amp; cell.detailTextLabel.numberOfLines == 0)) { [cell layoutIfNeeded]; // this is necessary on iOS 7 to give the cell's text labels non-zero frames // Calculate the height of the cell based on the text [cell.textLabel sizeToFit]; [cell.detailTextLabel sizeToFit]; cellHeight = CGRectGetHeight(cell.textLabel.frame) + CGRectGetHeight(cell.detailTextLabel.frame); // This is the best I can come up with for this :( if (self.tableView.separatorStyle != UITableViewCellSeparatorStyleNone) { cellHeight += 1.0; } } [sectionCellHeights addObject:[NSNumber numberWithFloat:cellHeight]]; } [cellHeights addObject:sectionCellHeights]; } _cellHeights = cellHeights; } </code></pre> <p>In <code>-tableView:cellForRowAtIndexPath:</code>, create a cell as normal and fill out the textLabel, without performing any layout (as Bob mentioned elsewhere on this page: "UITableViewCells perform all their layout stuff right after this is called, so your efforts at best are futile, at worst they screw things up"). The important thing is to set the label's <code>numberOfLines</code> property to 0 to allow for dynamic height.</p> <pre><code>- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; // Assign any fonts or other adjustments which may affect height here // Enable multiple line support so textLabel can grow vertically cell.textLabel.numberOfLines = 0; } // Configure the cell... cell.textLabel.text = [self.texts objectAtIndex:indexPath.section]; return cell; } </code></pre> <p>In <code>-tableView:heightForRowAtIndexPath:</code>, return the pre-calculated cell heights:</p> <pre><code>- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { if (_cellHeights) { NSArray *sectionCellHeights = [_cellHeights objectAtIndex:indexPath.section]; return [[sectionCellHeights objectAtIndex:indexPath.row] floatValue]; } return self.tableView.rowHeight; // default } </code></pre> <p>In <code>-viewDidAppear:</code>, trigger recalculation of the cell heights and refresh the table. Note that this must happen at <code>-viewDidAppear:</code> time, not <code>-viewWillAppear:</code> time, because at <code>-viewWillAppear:</code> time, the cells are not yet configured for the geometry of the table view.</p> <pre><code>- (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self _recalculateCellHeights]; [self.tableView reloadData]; } </code></pre> <p>In <code>-didRotateFromInterfaceOrientation:</code>, do the same thing:</p> <pre><code>- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { [self _recalculateCellHeights]; [self.tableView reloadData]; } </code></pre> <p>It is not necessary to do anything special in <code>-tableView:willDisplayCell:forRowAtIndexPath:</code>.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload