1
votes

I have spent weeks trying to get this to work and have looked at many different suggestions on here, Apple developer forums, Google etc. and am still pulling my hair out. Any help would be greatly appreciated.

I have a ViewController that contains a TableView. It is not a full-screen tableView.

The tableView is built with customTableViewCells that each holds a CollectionView. The problem I have is that the self-sizing collectionView and the self-sizing tableView rows just don't seem to work. I've found a few options online but they only seem to partially work.

This is what I get when it initially runs:

enter image description here

I'm including a link to the project file as that's probably easier than copying code into here: https://www.dropbox.com/sh/7a2dquvxg62aylt/AACK_TjDxT9eOShZaKi7vLYga?dl=0

Some of the online 'workarounds' don't work in all cases - e.g. if you tap one of the buttons inside the CollectionView, it is removed and the collectionView (and subsequently the TableView) should resize but again I can't get this to consistently work.

Some of the solutions I've tried:

UICollectionView Self Sizing Cells with Auto Layout

UICollectionView inside a UITableViewCell -- dynamic height?

Dynamic height for a UITableView based on a dynamic collection view

Auto-sizing UITableViewCell which contains UICollectionView

Any help would be greatly appreciated.

1
I think adding the relevant code for those who don't want to skim through the entire project would be a good idea.rs7
Noted @rs7 - I actually created a simplified Xcode project file that only has the coe for this question/problem. I think that’s part of the problem with referencing some of the other solutions when they aren’t looking at the full, interconnecting code. Sorry, I was trying to be helpful rather than hinder.Anthony
No worries at all @Anthony. It was just a suggestion not a criticism.rs7

1 Answers

1
votes

I figured out what was wrong. There are a couple things that needed to be fixed:

  1. You need to set an estimated row height (with an actual value) and set the row height as automatic. You did the opposite. So please, replace your setupTableView function with this one below. Also, yo need to set the delegate which you didn't do. So make sure you add UITableViewDelegate next to your class name, and assign it inside of setupTableView() like I did below.

    func setupTableView() {
        view.addSubview(tempTableView)
        NSLayoutConstraint.activate([
            tempTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tempTableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 200),
            tempTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tempTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -200)
        ])
        tempTableView.rowHeight = UITableView.automaticDimension
        tempTableView.estimatedRowHeight = 90
        tempTableView.register(CustomTableViewCell.self, forCellReuseIdentifier: CustomTableViewCell.cellIdentifier)
        tempTableView.dataSource = self
        tempTableView.delegate = self
        tempTableView.translatesAutoresizingMaskIntoConstraints = false
        tempTableView.layoutIfNeeded()
    }
    
  2. Inside cellForRowAt, add layoutIfNeeded to make sure the cells are auto sized properly:

    extension ViewController: UITableViewDataSource, UITableViewDelegate {
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            2
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tempTableView.dequeueReusableCell(withIdentifier: CustomTableViewCell.cellIdentifier) as! CustomTableViewCell
            cell.layoutIfNeeded()
            return cell
        }
    }
    
  3. Your CustomCollectionView needs to subclass UICollectionViewDelegateFlowLayout.

  4. Make sure you assign your configuredCollectionViewLayout variable to your collectionView:

    func setupCollectionView() {
        backgroundColor = .systemPink
        isScrollEnabled = false
        register(CustomCollectionViewCell.self, forCellWithReuseIdentifier: CustomCollectionViewCell.cellIdentifier)
        dataSource = self
        self.collectionViewLayout = configuredCollectionViewFlowLayout
    }
    
  5. Finally, add these 3 overrides to make your collectionView auto size based on its content (inside CustomCollectionView):

    override var intrinsicContentSize: CGSize {
         self.layoutIfNeeded()
         return self.contentSize
     }
    
     override var contentSize: CGSize {
         didSet{
             self.invalidateIntrinsicContentSize()
         }
     }
    
     override func reloadData() {
         super.reloadData()
         self.invalidateIntrinsicContentSize()
     }
    

Here is how you trigger a cell redraw upon clicking a button. I'm not using a protocol, but please do, this is just to show you the idea:

func tokenTapped(withTitle title: String) {
    guard let indexOfItemToRemove = tokens.sampleData.firstIndex(where: {$0.name == title}) else { return }
    tokens.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 {
                print("tableview updates")
                tableView.beginUpdates()
                tableView.endUpdates()
                DispatchQueue.main.async {
                    tableView.layoutIfNeeded()
                }
            }
        }
    }
}