There are many blogs and articles talked about this issue and resolution on Internet. But any of them has this or that problems in those solutions. So I took several hours on researches and got a perfect result, at least I think so. 🙂
Thought about device orientation change, table width and cell width will change. And since we are already use Auto Layout here, it is not a good practice to design cell with any implicit width, say, 320. in Interface Builder. Almost all solutions on Internet assumed 320 width implicitly so those solutions would fail on either landscape orientation, or, iPad if the app is a universal app and you use same cell design for both iPhone and iPad.
So, the problem is in [tableView: heightForRowAtIndexPath:] delegate method. This delegate method is required for iOS7 or the app would crash. To return a correct height value of the cell, we need those steps:
- Set cell width to table width after loaded the cell from xib.
- Set values for widgets including multi-line UILabel inside cell
- Call [cell setNeedsLayout] and [cell layoutIfNeeded]. After those 2 calls, system’s layout engine would set correct width for UILabels. Note at this time the label’s preferredMaxLayoutWidth is still zero or any value left from last using so the layout engine cannot wrap lines and adjust label’s frame correctly.
- Set label.preferredMacLayoutWidth = CGRectGetWidth(label.frame)
- Call [cell setNeedsLayout] and [cell layoutIfNeeded] again. This time labels have correct preferredMaxLayoutWidth and the layout engine would adjust label’s frame correctly
- Now you can call [systemLayoutSizeFittingSize:UILayoutFittingCompressedSize] to get correct height.
Solutions on Internet usually don’t do step 1 and 5. In an invariant screen size they can get correct display but they are not a real solution for other cases.
However, from iOS8 there is a simple solution. Apple solved the issue. Just returning UITableViewAutomaticDimension or just not implementing the method will get correct result. But for apps having to maintain compatibility with iOS7, we still have to do something like this.