Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Auto Layout with relative constraints not affecting systemLayoutSizeFittingSize: for UITableViewCell

I have a UITableView with dynamic height UITableViewCells and I'm using auto layout to calculate the heights as per this excellent answer on the topic. So far so good.

I'm adapting the app to work with the larger iPhone (6 and 6 Plus) screen sizes and it's mostly pretty straightforward.

However, in some of my cells I have an image that I want to span the whole width of the cell, and I want the image's height to be proportional to the width of the image (0.55 * the width, to be specific). Up to now I had the width and height of the image hard coded in the auto layout constraints, based on the standard 320px portrait table view width pre iPhone 6/6 Plus.

I thought it would be straightforward to add a relative height constraint like so (I'm using PureLayout):

[self.myImage autoMatchDimension:ALDimensionHeight 
                     toDimension:ALDimensionWidth 
                          ofView:self 
                  withMultiplier:0.55f 
                        relation:NSLayoutRelationGreaterThanOrEqual];

If you're not familiar with PureLayout, this translates to a call to

[NSLayoutConstraint constraintWithItem: attribute: relatedBy: toItem: attribute: multiplier: constant:0.0f]

There are other constraints that pin it's edges to the superview, which is the UITableViewCell contentView.

However, when I call systemLayoutSizeFittingSize: on the cell's contentView it seems to totally ignore the relative height constraint, and the resulting cell height is far too small to fit the image.

If I set an explicit height constraint instead of a relative one, there's no problem. Similarly, if I subclass UIImageView and return an explicit size in intrinsicContentSize, there is no problem.

I even tried the following in my UIImageView subclass:

- (void) layoutSubviews {
    [super layoutSubviews];    
    self.intrinsicSizeForAutolayout = self.frame.size;    
    [super layoutSubviews];
}

- (CGSize)intrinsicContentSize {
    return self.intrinsicSizeForAutolayout;
}

where intrinsicSizeForAutolayout is a property I defined for the purpose. I thought this might work similarly to the way that setting preferredMaxLayoutWidth for UILabels solves a similar problem.

But no. It doesn't work.

It would seem I have little alternative to use ugly screen width checking code to conditionally set a fixed height constraint depending on the screen width, something I really wanted to avoid, as it kind of defeats the purpose of using auto layout in the first place.

like image 887
mluisbrown Avatar asked Mar 18 '23 20:03

mluisbrown


1 Answers

The width of the cell's contentView doesn't any have constraints by default (its width is set by the SDK only when it is added to the table view), so when you call systemLayoutSizeFittingSize: on it, the constraint solver assumes it's valid to compress the width as much as needed when trying to find a valid solution, which of course results in the wrong height.

To fix this, you can add a single constraint to the contentView that fixes its width to the final cell/table view width. This works because that constraint will be factored into the cell sizing layout pass and result in systemLayoutSizeFittingSize: working as expected.

Using PureLayout, I recommend doing something like:

[UIView autoSetPriority:UILayoutPriorityRequired - 1 forConstraints:^{
    [cell.contentView autoSetDimension:ALDimensionWidth toSize:CGRectGetWidth(tableView.bounds)];
}];

Note that it's not a bad idea to set a priority of less than Required for the constraint, just because this is simply to aid the sizing operation, and you don't want an exception if this gets broken (perhaps by a future SDK change in how table view cells work). But this probably doesn't matter.

Also, as usual you probably want to make sure this constraint is only added once (not every time tableView:heightForRowAtIndexPath: is called) -- this should be easy to do.

See here for a specific solution to your fork of the example project: https://gist.github.com/smileyborg/0a2082a4d26fcc7fde4d

like image 91
smileyborg Avatar answered Apr 25 '23 06:04

smileyborg