tableviewCell self-sizing cells with embedded collectionViews

I have a self-sizing collectionView inside of a UITableViewCell.

The structure is as follows. A:

ViewController: UIViewController()

which contains a tableView property declared as:

tableView: UITableView().

This contains a number of customTableViewCell/s that are a custom subclass declared as:

CustomTableViewCell: UITableViewCell().

Each tableViewCell contains a customCollectionView declared as:

CustomCollectionView: UICollectionView().

This collectionView contains a number of customCollectionViewCell/s declared as:

CustomCollectionViewCell: UICollectionViewCell()



Inside the ViewController class, the tableView is configured with the following:

Code Block
tableView.rowHeight          = UITableView.automaticDimension
tableView.estimatedRowHeight = 40


Inside the customTableViewCell class, where the customCollectionView is declared, the customCollectionView is constrained inside the customTableViewCell to the customTableViewCell.contentView using the top, bottom, leading and trailing anchors.


QUESTION 1:


The only way I can get the tableView to load correctly on initial load is to call:
 
Code Block
override func viewDidAppear(_ animated: Bool) {
        tableView.beginUpdates()
        tableView.endUpdates()
}

First question - Is this the correct way of getting the parent tableView to update itself? It works but because it is being called on viewDidAppear, the resizing activity can be seen by the user. If I try and call it in viewWillAppear, then this is too early and the content of the customTableViewCell hasn't finished loading and being laid out.



QUESTION 2:


When the user interacts with the content inside a customCollectionViewCell, the collectionView content changes - customCollectionViewCells are either added or removed from the customCollectionView - this causes the overall customCollectionView to either grow or shrink vertically in height.

When this happens, even though autolayout has pinned the customCollectionView to the edges of the customTableViewCell.contentView , the customTableViewCell is not automatically resizing itself even though it is a self-sizing cell.

I've looked around and there are a lot of tableView.reloadData() recommendations - something Apple suggests not using.

Alternatively, I've been able to get it to work two ways:

Option 1:

In the customCollectionView class, calling self.superview?.superview?.superview to get to the UITableView to then update the tableView. This is the method I'm using when the content inside the customCollectionView is updated:


Code Block
func removeItem(titleOfItem: String) {
        guard let indexOfItemToRemove = sampleData.firstIndex(where: {$0.title == titleOfItem}) else { return }
        sampleData.remove(at: indexOfItemToRemove)
        DispatchQueue.main.async {
            self.performBatchUpdates({
                self.deleteItems(at: [IndexPath(item: indexOfItemToRemove, section: 0)])
            }) { (true) in
                if let tableView = self.superview?.superview?.superview as? UITableView {
                    tableView.beginUpdates()
                    tableView.endUpdates()
                    DispatchQueue.main.async {
                        tableView.layoutIfNeeded()
                    }
                }
            }
        }
    }


Option 2:

By using a delegate method. So in the customCollectionView class:

  
Code Block
func removeItem(titleOfItem: String) {
        guard let indexOfItemToRemove = sampleData.firstIndex(where: {$0.title == titleOfItem}) else { return }
        sampleData.remove(at: indexOfItemToRemove)
            DispatchQueue.main.async {
                self.performBatchUpdates({
                    self.deleteItems(at: [IndexPath(item: indexOfItemToRemove, section: 0)])
                }) { (true) in
                    self.updateDelegate?.needToUpdate()
                }
            }
        }


Then, in the ViewController class, via the delegate, I do the following:

Code Block
func customCollectionViewUpdatesHaveOccurred() {
        tableView.beginUpdates()
        tableView.endUpdates()
    }


So the second question is - am I missing something because autolayout is not automatically resizing the customTableViewCell based on its content? Or are my above workarounds the correct way of doing this and if so, which is the better option? If not, then how should I be doing this?

Apologies if this is too long, I've never posted on here and just trying to be super clear. Happy to share the actual code/project file if that helps.
This code shouldn't be required. UITableView automatically queries its data source and lays out is content when it appears on screen.
Code Block swift
override func viewDidAppear(_ animated: Bool) {
tableView.beginUpdates()
tableView.endUpdates()
}


Why are you using UITableView with UICollectionViews embedded in its cells? Have you thought about replacing it by UICollectionView and UICollectionViewCompositionalLayout? This might make it easier to lay out content and reason about updates to the data.
I can potentially move it across to a UICollectionView but at the moment I've been using a UITableView as I don't really have any other complexity. With your question re viewDidAppear, I need to call this:

Code Block
override func viewDidAppear(_ animated: Bool) {
tableView.beginUpdates()
tableView.endUpdates()
}

because if I don't, then when the tableView initially loads, it doesn't resize the tableView cells accordingly and therefore show the embedded UICollectionView.
I might be wrong about this, but in my experience, a collection view with self-sizing cells does not completely lay itself out in a single layout pass/run of the run loop. So your table view configures the cell, the collection view starts laying itself out, but then the call returns. Sometime later, the table view measures the cell. Some time after that, the collection view reaches its actual proper size. So, things are happening in the wrong order.

In the past, the only way I’ve gotten this to work is if the collection view is the top level view of a view controller (it can be a child view controller). In that case, I was able to hook into viewDidLayoutSubviews as a signal that collection view layout was really complete. Then you can pass that along to a delegate that tells the table view to reload the appropriate cell.

Needless to say this was a nightmare to actually get working so if your deployment target allows you to use compositional layout instead, that definitely seems like a direction to investigate!
tableviewCell self-sizing cells with embedded collectionViews
 
 
Q