0

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.

Anthony
  • 349
  • 1
  • 2
  • 16
  • I think adding the relevant code for those who don't want to skim through the entire project would be a good idea. – rs7 Jun 21 '20 at 20:12
  • 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 Jun 21 '20 at 22:24
  • No worries at all @Anthony. It was just a suggestion not a criticism. – rs7 Jun 21 '20 at 22:54

1 Answers1

1

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()
                }
            }
        }
    }
}
Wai Ha Lee
  • 7,664
  • 52
  • 54
  • 80
rs7
  • 1,423
  • 1
  • 5
  • 15
  • Thanks but I just tried this and its not working for me. Whilst the collectionView size is now bigger, its height is bigger than the content so there is extra space at the bottom. Also, both the collectionView and the tableViewCell aren't resizing as a user taps on the token buttons and they are removed from the collectionView. – Anthony Jun 22 '20 at 00:19
  • You are correct. Only the first cell shows a weird behavior though which I found a fix for -- the subsequent cells work just fine. I've updated my answer. In setupTableView(), call layoutIfNeeded() for the tableView. – rs7 Jun 22 '20 at 00:40
  • For the second issue you mentioned, you can simply define a protocol that your viewController adopts and every time you click on a button you trigger a tableView update via tableView.beginUpdates() tableView.endUpdates(). – rs7 Jun 22 '20 at 00:45
  • Thank. Updated and tested with that. You're correct, collectionView is now resizing when you remove tokens (by tapping on them) but the tableViewCell is not updating as its content (i.e. the collectionView) changes. – Anthony Jun 22 '20 at 00:45
  • I've updated my answer again to show you how to trigger a cell layout update after tapping a token. – rs7 Jun 22 '20 at 00:53
  • Thanks. I saw that after I'd responded. I've included the recommendations and it is partially working. 1. Views are initially loaded and appear correctly although xCode is throwing up layout errors - any suggestions?. 2. Using the delegate method to update the tableview partially works but I ended up adding it to the completion block in the customCollectionView - THANK YOU! – Anthony Jun 22 '20 at 01:06
  • You are welcome. For the layouterrors, if you notice, one of the error is an encapsulated layout height error. This sort of error you can fix by decreasing the vertical constraints priority: `constraint.priority = UILayoutPriority(999)`. You have 3 constraints to apply that to inside the tableViewCell (contentView bottom and label top, label bottom and collectionView top, collectionView and contentView bottom). For the other error complaining about the tableView being laid out too early, you can ignore it's a bug on Apple's part. – rs7 Jun 22 '20 at 01:16
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/216392/discussion-between-anthony-and-rs7). – Anthony Jun 22 '20 at 01:33