Reputation: 890
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:
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.
Upvotes: 3
Views: 1773
Reputation: 1630
I figured out what was wrong. There are a couple things that needed to be fixed:
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()
}
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
}
}
Your CustomCollectionView needs to subclass UICollectionViewDelegateFlowLayout.
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
}
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()
}
}
}
}
}
Upvotes: 2