UITableViewCell remake constraints when reuse problem

746 Views Asked by At

I have a problem with reusing a cell.

In my cell there is a view that changes the constraints depending on its state. By default, cell height = 0, when the user clicks on the button, it changes its constraints top, leading, trailing and bottom. The problem is that if I expand, press expand view in one cell, scroll down (or up), then another cell will also be expanded.

I change the variable responsible for uncovering the cell in the prepareForReuse method, and it doesn't help :(

My solution used to be like this: I redid the constraints in the configure method (it is called in the cellForRow method in VC), and it worked, BUT there were lags when scrolling, apparently, because I change the constraints every time a cell is configured and reused Are there ways to avoid lag when scrolling? Is there another way to do this?

I use SnapKit for make layout.

2

There are 2 best solutions below

0
On BEST ANSWER

I solved the problem by dividing the states into different cells

0
On

I would try avoiding making the cell have a height of zero. This may mess with table view and create an overhead in multiple situations. Since cells are dequeued to reduce resource consumptions you should not produce zero-sized cells as theoretically you can see infinite number of such cells in screen. So basically you can easily have a situation where hundreds of cells are being processed by your table view which user is not even seeing.

An alternative to zero-height cell is to insert/delete cells from table view. There are even animations for that already in place. To achieve this you need to correct your numberOfRows method and your cellForRowAt where both of them need to ignore "hidden" cells. Beside that you basically just need something like

func hideCell(indexPath: IndexPath) {
    tableView.beginUpdates()
    self.items[indexPath].isHidden = true
    tableView.deleteRows(at: [indexPath], with: .automatic)
    tableView.endUpdates()
}

func showCell(indexPath: IndexPath) {
    tableView.beginUpdates()
    self.items[indexPath].isHidden = false
    tableView.insertRows(at: [indexPath], with: .automatic)
    tableView.endUpdates()
}

This should fix most (all) of your issues. But if you still want to stay with having small/zero sized cells there are other ways to fix your issue.

When you change your constraints you should also call (maybe you already do that but did not post it)

cell.remakeParentCommentConstraints()
tableView.beginUpdates()
tableView.endUpdates()

and make sure that when cellForRow is being called you also call cell.remakeParentCommentConstraints() because as cell is dequeued you can not know what state the previous cell was in.

Even when doing all of this correctly you may experience jumping. This is now because estimated height logic is not very smart by default and you need to improve it. A quick fix is to cache your cell heights:

private var cellHeightCache: [IndexPath: CGFloat] = [:]

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return cellHeightCache[indexPath] ?? 44.0
}

func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    cellHeightCache[indexPath] = cell.bounds.height
}

The part with 44.0 should be whatever you have currently set as estimated row height. This trick may get a bit more complicated if you insert/delete rows (as in first solution). As you also need to insert/remove heights from cache. It is doable, just not as easy.