@rdelmar has the right approach. You definitely need to reset the preferredMaxLayoutWidth on each label before you call systemLayoutSizeFittingSize on the contentView. I also use a UILabel subclass with the layoutSubviews method as he demonstrates.
The main downside to an autolayout approach like this is the overhead. We're effectively running autolayout three times for each cell that will be displayed: Once to prepare the sizing cell for systemLayoutSizeFittingSize (size it and set preferredMaxLayoutWidth on each sublabel), again with the call to systemLayoutSizeFittingSize, and again on the actual cell we return from cellForRowAtIndexPath.
Why do we need/want the first autolayout pass? Without it we don't know what width to set our child labels preferredMaxLayoutWidth values to. We could hardcode the values as in @rdelmars example (which is fine) but it's more fragile if you change your cell layout or have lots of cell types to deal with.
If the main issue is recalculating on orientation change then the code below can likely be optimized to run the first layout pass just once per orientation change.
Here's the pattern I use, which negates the need to manipulate the cell controls in the view controller. It's more encapsulated but arguably more expensive.
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// assumes all cells are of the same type!
static UITableViewCell* cell;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cell = [tableView dequeueReusableCellWithIdentifier: @"label_cell"];
});
// size the cell for the current orientation. assume's we're full screen width:
cell.frame = CGRectMake(0, 0, tableView.bounds.size.width, cell.frame.size.height );
// perform a cell layout - this runs autolayout and also updates any preferredMaxLayoutWidths via layoutSubviews in our subclassed UILabels
[cell layoutIfNeeded];
// finally calculate the required height:
CGSize s = [cell.contentView systemLayoutSizeFittingSize: UILayoutFittingCompressedSize];
return s.height + 1; // +1 because the contentView is 1pt shorter than the cell itself when there's a separator. If no separator you shouldn't need +1
}
along with:
@interface TSLabel : UILabel
@end
@implementation TSLabel
- (void)layoutSubviews
{
self.preferredMaxLayoutWidth = CGRectGetWidth(self.bounds);
[super layoutSubviews];
}
@end