1

I have a tableview cell with two labels and a small image. If the labeltexts are short, all fits in one row like this:

.________________________________________.
|                                        |
|  [firstlabel] [img] [secondlabel]    > |
|________________________________________|

However, if the labeltexts are getting longer, the image and the second label should move to the second line:

.________________________________________.
|                                        |
|  [firstveryverylonglabel]            > |
|   [img] [secondverylonglabel]          |
|________________________________________|

How would I do this with autolayout?

The first label is easy - just add constraints for left and top. Labels are always only one line heigh, and get truncated in the middle if the text gets way too long.

The image must always be in front of the second label (default space) horizontally, and both centered vertically. The image size should scale with the dynamic text font size (preferredFontForTextStyle:UIFontTextStyleBody) of the labels, so that if the user chooses large text the image also gets drawn larger.

The cell height should of course grow if two lines are needed.

Bonus Points if setting the necessary constraints would be possible in Interface Builder, but if not then I could set them with code in the init routine of the tableview cell.

My app should run under iOS 7 and later - no need for iOS 6. So I must compute the height in tableview:heightForCellAtIndexPath:, and will use a hidden cell to let Autolayout do its magic there.

Marc
  • 49
  • 6

1 Answers1

2

I ended with setting constraints in code. I embedded the labels and the image in another view, then implemented -updateConstraints for the cell and the view.

In -tableView:heightForRowAtIndexPath:, I first let Autolayout make a first pass on the static hidden cell (just used for measuring) to set up the contentView by calling layoutIfNeeded, then force it to update the constraints by calling setNeedsUpdateConstraints followed by updateConstraintsIfNeeded, and finally measure the height of the cell with systemLayoutSizeFittingSize:UILayoutFittingCompressedSize.

In my view's updateConstraints method, I first try to arrange the cells side-by-side with

  [self removeConstraints];
  [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:
        @"V:|-margin-[first]-margin-|"
    options:0 metrics:metrics views:views]];
  [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:
        @"H:|-margin-[first]-[image]-[second]-margin-|"
    options:NSLayoutFormatAlignAllCenterY metrics:metrics views:views]];

Then I call systemLayoutSizeFittingSize:UILayoutFittingCompressedSize on the view and compare the width to the maximum possible space inside the contentView, given its fixed horizontal start position. If it's too wide, I need to set a two-line constraint set:

  [self removeConstraints];
  [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:
        @"V:|-margin-[first]-[second]-margin-|"
    options:0 metrics:metrics views:views]];
  [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:
        @"H:|-margin-[first]-margin-|"
    options:0 metrics:metrics views:views]];
  [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:
        @"H:|-margin-[image]-[second]-margin-|"
    options:NSLayoutFormatAlignAllCenterY metrics:metrics views:views]];

In the cell's updateConstraints method, I just call setNeedsUpdateConstraints on the view to make sure that the view is recalculated after the cell's size changed (e.g. when rotating portrait<=>landscape). And that's it.

Marc
  • 49
  • 6