16
votes

So I am able to set up dynamic height sizing with auto layout when I have only one label that changes the height based on the length of the string. My problem is that if I add another UILabel that should do the same, things don't work out.

I am setting both the Content Hugging priority and the Compression Resistance to 1000 for both == I get warnings of ambiguity

If I set the Content Hugging (Vertical) for the second UILabel to 999 or 250 then it works great but only if the second label has 2 or more line. If the second label is blank or has only one line, the heightForRowAtIndexPath systemLayoutSizeFittingSize:UILayoutFittingCompressedSize height returns large values, and the cells have large blank spaces.

I also played around with the Intrinsic Size: Default or Placeholder(with couple of heights and widths) but it does not help either.

Anyone any suggestions what can be done?

3

3 Answers

19
votes

I got it working finally. The solution was for me to explicitly set the Preferred Width to the current frame width. So basically checking the Explicit check mark in the Label > Preferred Width in the Size Inspector.

ref: http://www.raywenderlich.com/73602/dynamic-table-view-cell-height-auto-layout download the sample code and see the storyboard setup.

1
votes

For me it took a combination of several things.

  • Besides setting the content hugging and resistance correct
  • I had to make a subclass of UILabel to handle the preferredMaxLayoutWidth of the label
  • and correct a bug in the intrinsicContentSize as well

The UILabel subclass ended up looking like this:

@implementation LabelDynamicHeight

- (void)layoutSubviews
{
    [super layoutSubviews];

    self.preferredMaxLayoutWidth = self.frame.size.width;

    [super layoutSubviews];
}

- (void)setBounds:(CGRect)bounds
{
    [super setBounds:bounds];

    if (self.numberOfLines == 0)
    {
        CGFloat boundsWidth = CGRectGetWidth(bounds);
        if (self.preferredMaxLayoutWidth != boundsWidth)
        {
            self.preferredMaxLayoutWidth = boundsWidth;
            [self setNeedsUpdateConstraints];
        }
    }
}

- (CGSize)intrinsicContentSize
{
    CGSize size = [super intrinsicContentSize];

    if (self.numberOfLines == 0)
    {
        // There's a bug where intrinsic content size may be 1 point too short
        size.height += 1;
    }

    return size;
}

@end

Besides this calling layoutIfNeeded on the cell before getting the height in tableView heightForRowAtIndexPath: made the constraints to be ready before using the systemLayoutSizeFittingSize: method on the contentView. Looking like this:

- (CGFloat)tableView:( UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = ...;
    CGFloat height = cell.frame.size.height;
    if (cell.dynamicHeight)
    {
        // https://stackoverflow.com/a/26351692/1840269
        [cell layoutIfNeeded];
        height = cell.frame.size.height;

        CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
        if (size.height > height)
            height = size.height;
    }
    return height;
}

References:

0
votes

For me setting content hugging priority and content compression resistance priority lower to one of the element worked just as expected.