title: Dynamic UILabel Heights/Widths in UITableViewCell in all Orientations
body: <p>My question essentially boils down to the <strong>best way to support dynamic heights of UILabel's (and I suppose other elements) in a UITableCell</strong>, and also <strong>correctly resize the label width/height and cell heights when rotating</strong>.</p>
  
  <p>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.</p>
  
  <p>I'm thinking this might go the route of <a href="https://stackoverflow.com/questions/1303695/adjusting-the-positions-of-the-labels-in-a-uitableviewcell">layoutSubviews</a>, but I'm not sure how you would combine that with the table needing to calculate the height of cells. <a href="https://stackoverflow.com/questions/6105489/autoresizing-uitablecells-contents-in-landscape">Another interesting post</a> 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.</p>
  
  <p>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.</p>
  
  <p><img src="https://i.stack.imgur.com/nRfo2.png" alt="Dynamic UILabel/Cell Height/Width"></p>
  
  <p>I've managed to get it working correctly, but not sure if its the correct method. </p>
  
  <h2>Here's a few of the things I learned:</h2>
  
  <ul>
  <li>The cell.frame in <code>cellForRowAtIndexPath</code> always gives its size in portrait mode. (ie. for the iphone app, it always reports 320).</li>
  <li>Storing a prototype cell for reference in a property has the same issue, always in portrait mode</li>
  <li>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</li>
  <li>The tableView.frame in <code>cellForRowAtIndexPath</code> gives its size in the correct orientation</li>
  <li>You need to call <code>[tableView reloadData]</code> on rotation. I couldn't find any other way to update the cell and label heights</li>
  </ul>
  
  <h2>Here are the steps I took to get this working for me:</h2>
  
  <ol>
  <li>Disable any autoresize masks on UILabels, as they interfere with getting the right label height for some reason</li>
  <li><p>On <code>viewDidLoad</code> I grab a reference to each label font, and a float for the label width percentage compared to the tableView in portrait</p>
  
  <pre><code>CWProductDetailDescriptionCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"DescriptionCell"];
  self.descriptionLabelWidthPercentage = cell.descriptionLabel.frame.size.width / 320;
  self.descriptionLabelFont = cell.descriptionLabel.font;
  </code></pre></li>
  <li><p>In <code>heightForRowAtIndexPath</code> I calculate the cell height using the tableView width and the percentage I already grabbed:</p>
  
  <pre><code>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;
  </code></pre></li>
  <li><p>In <code>cellForRowAtIndexPath</code> I calculate the frame for the label frame and update it</p>
  
  <pre><code>cell = [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);
  </code></pre></li>
  <li><p>In <code>didRotateFromInterfaceOrientation</code> you need to <code>[self.tableView reloadData]</code></p></li>
  </ol>
  
  <h2>Problems with this method:</h2>
  
  <ol>
  <li>You can't leverage the autoresizing masks and margins you <a href="http://screencast.com/t/Zpxf2HAcI" rel="nofollow noreferrer">might set in IB</a>
  
  <ul>
  <li>those don't seem to fire until after you've returned the cell in <code>cellForRowAtIndexPath</code> <strong>(not sure if this is exactly true)</strong></li>
  <li>they mess up the height calculation somehow, and your labels end up <a href="http://screencast.com/t/j6DJpVYv" rel="nofollow noreferrer">taller</a> than <a href="http://screencast.com/t/aD3AxDAU" rel="nofollow noreferrer">desired</a> in landscape.</li>
  </ul></li>
  <li>If your label's width isn't an equal percentage in <a href="http://screencast.com/t/GJglYeeKuD4" rel="nofollow noreferrer">portrait</a> and <a href="http://screencast.com/t/8i72MeRNDKSM" rel="nofollow noreferrer">landscape</a>, you'll need to know both percentages</li>
  <li>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. </li>
  <li>It just feels clumsy. I have a feeling I'm going to run into more headaches in editing mode with indenting.</li>
  </ol>
  
  <p><strong>So. What is the right way to do this? I've seen lots of SO questions, but none quite address this exactly.</strong></p>