2
votes

I am having troubles with an autosizing UITableViewCell which contains a UICollectionView. The view hierarchy is roughly like this

UITableViewCell
-- Content View
---- Custom View
------ UIStackView (vertical)
--------- UILabel
--------- UICollectionView (vertical flow layout)

When the UITableViewCell is created or reused, the collectionView receives a datasource which it then starts to process. I need all cells of the collectionView to show in the tableViewCell, so I observe changes to the collectionView's content size in the Custom View like this

    self.collectionView.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.old, context: nil)

Once the collectionView finished rendering its content, I read its contentSize and set the height constraint of the collection view accordingly. This works fine.

The problem now is that all values calculate correctly, but the cell size does not change in the tableView. There are no autolayout errors or warnings in the console, so I assume all constraints are correct. The label in the stackView is also autosizing and this works fine.

I can pass info about the new contentSize of the stackView all the way up to the TableViewController, but I don't know what to call where to force autolayout to recalculate the height of an individual cell.

If I call tableView.reloadData() at the controller level if the height constraint of the collectionView in a cell does not match its content size, it does work. But it causes a delay and is obviously too expensive.

If I pass the cell that changed to the tableView and ask it to reload just the cell, the tableView does not find the indexPath of the cell (probably because it is still off screen). So this approach does not work.

Thanks!

2
please provide expected UI screen shot. i think it is possible with single scroll view, you don't need nesting two scrollview.SPatel
@SPatel There are no two scrollViews. The tableView has the scrollView, the collectionView is sized to display all cells, so it is not scrolling.Georg
I stumbled upon this scenario as well, but I realized it could be heavy for doing this operation as it might not always possible your collectionview cells size layout before tableview cells. In most use case scenario you can maintain a datasource of those heights by calculating them earlier as we only will be specifying collectionview cell size.Amber K
@Georg i personally suggest you that don't use nested scrollview if possible.SPatel
Apart from that, would like to point that you can fall into the common bug while nesting scrollviews, you should call collectionview.reloadData() in tablview's dequeue method, because while reusing table cells collectionview get reused its own dequeue method isn't called. This will help - ashfurrow.com/blog/…Amber K

2 Answers

2
votes

First of all thanks to everybody for their responses!

After two days of pulling out my hair, it looks like UITableViewCell layout not updating until cell is reused solved it for me.

The problem was the missing

    cell.layoutIfNeeded()

at the end of

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 

Here you can see what it looks like now. The setup is like in the original question.

Again thanks everybody for your pointers!

Here you can see the result

0
votes

Even thought this is an already answered question. But I have found this easy method in this tutorial to resize the UITableViewCell height according to the UICollectionView height size.

Add a height constraint to the UICollectionView and create an outlet for it in the UITableViewCell and don't forget to disable UICollectionView Scrolling and set its flow to vertical.

@IBOutlet weak var collectionViewHeight: NSLayoutConstraint!

In the UITableViewDelegate cellForRow method use these

cell.frame = tableView.bounds

cell.collectionView.reloadData()

cell.layoutIfNeeded()

cell.collectionViewHeight.constant = cell.collectionView.collectionViewLayout.collectionViewContentSize.height

I can tell it is working like charm and no need for observes.

Link for full tutorial