My question essentially boils down to the best way to support dynamic heights of UILabel's (and I suppose other elements) in a UITableCell, and also correctly resize the label width/height and cell heights when rotating.
I'm aware of how to get the expected height of UILabels, and size the heights accordingly, but things seem to get pretty tricky when you support landscape orientation as well.
I'm thinking this might go the route of layoutSubviews, but I'm not sure how you would combine that with the table needing to calculate the height of cells. Another interesting post sets the frame manually on init to make sure they are doing calculations off a known quantity, but that only addresses part of the issue.
So here's an image of what I'm dealing with, with red arrows pointing to the dynamic height labels, and blue arrows pointing towards the cells that will change height.
I've managed to get it working correctly, but not sure if its the correct method.
Here's a few of the things I learned:
- The cell.frame in
cellForRowAtIndexPath
always gives its size in portrait mode. (ie. for the iphone app, it always reports 320). - Storing a prototype cell for reference in a property has the same issue, always in portrait mode
- autoresizing masks for the width of labels seem to be useless in this use case, and in fact cause issues with the calculated height on rotation
- The tableView.frame in
cellForRowAtIndexPath
gives its size in the correct orientation - You need to call
[tableView reloadData]
on rotation. I couldn't find any other way to update the cell and label heights
Here are the steps I took to get this working for me:
- Disable any autoresize masks on UILabels, as they interfere with getting the right label height for some reason
On
viewDidLoad
I grab a reference to each label font, and a float for the label width percentage compared to the tableView in portraitCWProductDetailDescriptionCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"DescriptionCell"]; self.descriptionLabelWidthPercentage = cell.descriptionLabel.frame.size.width / 320; self.descriptionLabelFont = cell.descriptionLabel.font;
In
heightForRowAtIndexPath
I calculate the cell height using the tableView width and the percentage I already grabbed:case TableSectionDescription: CGFloat labelWidth = self.tableView.frame.size.width * self.descriptionLabelWidthPercentage; CGSize newLabelFrameSize = [self sizeForString:self.product.descriptionText WithConstraint:CGSizeMake(labelWidth, MAXFLOAT) AndFont:self.descriptionLabelFont]; return newLabelFrameSize.height + kTextCellPadding;
In
cellForRowAtIndexPath
I calculate the frame for the label frame and update itcell = [tableView dequeueReusableCellWithIdentifier:@"DescriptionCell"]; ((CWProductDetailDescriptionCell *)cell).descriptionLabel.text = self.product.descriptionText; CGRect oldFrame = ((CWProductDetailDescriptionCell *)cell).descriptionLabel.frame; CGFloat labelWidth = self.tableView.frame.size.width * self.descriptionLabelWidthPercentage; CGSize newLabelFrameSize = [self sizeForString:self.product.descriptionText WithConstraint:CGSizeMake(labelWidth, MAXFLOAT) AndFont:((CWProductDetailDescriptionCell *)cell).descriptionLabel.font]; ((CWProductDetailDescriptionCell *)cell).descriptionLabel.frame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y, labelWidth, newLabelFrameSize.height);
In
didRotateFromInterfaceOrientation
you need to[self.tableView reloadData]
Problems with this method:
- You can't leverage the autoresizing masks and margins you might set in IB
- If your label's width isn't an equal percentage in portrait and landscape, you'll need to know both percentages
- You need to know/calculate your labels width percentages. Ideally you would be able to calculate these on the fly after an auto-resizing mask has done its thing.
- It just feels clumsy. I have a feeling I'm going to run into more headaches in editing mode with indenting.
So. What is the right way to do this? I've seen lots of SO questions, but none quite address this exactly.