UILabel in UITableViewCell with auto layout has wrong height

Since you're constraining the label's width, the intrinsicContentSize honors that width and adjusts the height. And this sets up a chicken and egg problem:

  1. The cell's Auto Layout result depends on the label's intrinsicContentSize
  2. The label's intrinsicContentSize depends on the label's width
  3. The label's width depends on the cell's Auto Layout result

So what happens is that the cell's layout is only calculated once in which (2) is based on the static width in the XIB file and this results in the wrong label height.

You can solve this by iterating. That is, repeat the Auto Layout calculation after the label's width has been set by the first calculation. Something like this in your custom cell will work:

- (void)drawRect:(CGRect)rect
{
    CGSize size = self.myLabel.bounds.size;
    // tell the label to size itself based on the current width
    [self.myLabel sizeToFit];
    if (!CGSizeEqualToSize(size, self.myLabel.bounds.size)) {
        [self setNeedsUpdateConstraints];
        [self updateConstraintsIfNeeded];
    }
    [super drawRect:rect];
}

original solution does not work reliably:

- (void)layoutSubviews
{
    [super layoutSubviews];
    // check for need to re-evaluate constraints on next run loop
    // cycle after the layout has been finalized
    dispatch_async(dispatch_get_main_queue(), ^{
        CGSize size = self.myLabel.bounds.size;
        // tell the label to size itself based on the current width
        [self.myLabel sizeToFit];
        if (!CGSizeEqualToSize(size, self.myLabel.bounds.size)) {
            [self setNeedsUpdateConstraints];
            [self updateConstraintsIfNeeded];
        }
    });
}

Adding this in the cell subclass works:

- (void)layoutSubviews
{
    [super layoutSubviews];
    [self.contentView layoutIfNeeded];
    self.myLabel.preferredMaxLayoutWidth = self.myLabel.frame.size.width;
}

I found this on http://useyourloaf.com/blog/2014/02/14/table-view-cells-with-varying-row-heights.html.

Update 1: This answer was for iOS 7. I find auto layout in table view cells to be very unreliable since iOS 8, even for very simple layouts. After lots of experimentation, I (mostly) went back to doing manual layout and manual calculation of the cell's height.

Update 2: I've run some tests on iOS 9 and it seems that UITableViewAutomaticDimension finally works as advertised. Yay!


Stupid bug! I've lost almost one day in this problem and finally I solved It with Steven Vandewghe's solution.

Swift version:

override func layoutSubviews() {
    super.layoutSubviews()
    self.contentView.layoutIfNeeded()
    self.myLabel.preferredMaxLayoutWidth = self.myLabel.frame.size.width
}