How to get preferred AutoLayout height given a width for a collection view cell with multiline labels?
Asked Answered
C

1

2

I'm building a list view with self-sizing in my app using UICollectionView. Getting UICollectionViewFlowLayout to do a vertical list layout is a pain, so I'm writing my own UICollectionViewLayout subclass to do it.

My cells look a lot like regular table view cells - an image view on the left, a couple of labels vertically stacked in the center, and an accessory view on the right. The labels can wrap to a few lines and grow in font size according to the system font size setting, which is why they need to be self-sizing. Constraints are sensible - left to image view, image view to label, label to accessory, accessory to right, label to top and bottom.

To size my cells via preferredLayoutAttributesFittingAttributes, I need to get the desired height of the cell given the width of the collection view. I'd expect to use systemLayoutSizeFittingSize:horizontalPriority:verticalPriority: for this, passing a size like {desiredWidth, crazyLargeHeight}, UILayoutPriorityRequired for horizontal priority, and 1 for vertical priority. But the size I get back from systemLayoutFittingSize isn't sensible. The width is some previous width value from the cell (which doesn't necessarily match self.bounds.size.width or the passed-in layoutAttributes' size.width), and a height that works with that width. I also have tried passing a small height instead of a large one, and it still doesn't return the size it actually needs.

So how do I get the preferred height for the cell given a width value?

Sample code:

-(UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
  UICollectionViewLayoutAttributes *result = [super preferredLayoutAttributesFittingAttributes:layoutAttributes];
  CGSize exampleSize = CGSizeMake(layoutAttributes.size.width, 20);
  CGSize size = [self systemLayoutSizeFittingSize:exampleSize withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:1];
  result.size = size;
  return result;
}
Clearcut answered 16/7, 2018 at 22:6 Comment(4)
You need your self here to be self-sizing in the sense that it has sufficient internal constraints so that when systemLayoutSizeFittingSize, the internal constraints provide a solution to the size question. Would it help you to see an example that works?Daffi
" thought I might be misunderstanding systemLayoutSizeFittingSize." Well, it looks like you are misunderstanding it. But given the right conditions and the right call, it will do what you are hoping for.Daffi
@Daffi exactly. Which means I need to tinker.Clearcut
@Daffi and...turns out if you ask self.contentView for systemLayoutSizeFittingSize, it works fantastic. Posting an answer in case someone else has the same headache someday.Clearcut
C
1

Turns out if you ask the cell's content view for a size instead of the cell itself, it works. And you have to give a sample height that is smaller than will be needed.

-(UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
  UICollectionViewLayoutAttributes *result = [super preferredLayoutAttributesFittingAttributes:layoutAttributes];
  CGSize exampleSize = CGSizeMake(layoutAttributes.size.width, 20); //magic number for example purposes...my cell will definitely be taller
  CGSize size = [self.contentView systemLayoutSizeFittingSize:exampleSize withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:1];
  result.size = size;
  return result;
}

I'm guessing that the content view isn't pinned to the cell's bounds the way I expected. And this exact implementation will likely fall down if the content view is inset for some reason. But the key to solving this was the fact that having the cell contents pinned to the cell's content view does not mean that the cell itself will compute a size as you expect.

Clearcut answered 16/7, 2018 at 22:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.