Solved: UITableView Jumps On Cell Deletion

Alan Wang
Compass True North
Published in
4 min readAug 19, 2019

In the Compass app’s Listing page, we had a bug where collapsing rows that were partially offscreen resulted in a strange jump, and visual glitches.

Animation slowed down so you can see what’s happening.

For context, we are using a UITableView here with self-sizing cells. We are using the built in API for deleting cells when collapsing a section.

Estimated Cell Height

When searching this issue on StackOverflow, there are a lot of questions, but most of the answers did not work for me. For most people the problem is with estimatedCellHeight. UITableView uses the value assigned here as a temporary placeholder for your cell, and if the estimated height varies dramatically from the real height, your view may unexpectedly jump, as it switches from estimated to real height.

However, this was not our issue, since we were already performing caching of cell heights for use in estimation.

Hypothesis

I suspected the reason this jump was occurring was because the table view’s contentOffset was not updating appropriately.

When rows are deleted below the visible area, the contentSize shrinks, and content below the deleted rows are pulled up.

Deleting the blue view

In this scenario, because we are above the affected rows, contentOffset can remain the same, and we will appear to have not moved.

However, if the deleted rows are above the visible area, then it affects which rows are visible.

Deleting the pink view

Once again, the contentSize changes, and all rows below the deleted row are pulled up. However, because our contentOffset has not changed, it appears as if we have been scrolled further down.

In order to create the illusion that we are not moving, we must adjust the contentOffset by the same amount the contentSize has shrunk.

Deleting the pink view and adjusting contentOffset

My theory was that the system was not doing this automatically. In order to fix it, I would have to manually adjust the contentOffset and then animate the change at the same time the table view was animating the row deletions in order to give the appearance that nothing was moving.

Failed Attempts

Computing the necessary height adjustment was easy, but animating the change at the same time as the table view turned out to be impossible. The animation block created by table view’s .beginUpdates() and .endUpdates() method do not allow you to synchronize animating a content offset change with the row deletion animations. I tried many things, ranging from CATransaction, UIView.performWithoutAnimation, to manually adjusting the cell heights to zero — nothing worked.

A Closer Look

While attempting all of the above for 2 days, losing 20% of my life and 30% of my hair, I discovered something interesting in my print statements. It seemed like after my call to deleteRows, the table view’s contentOffset was actually different than when it started. Apple was doing its job and adjusting the offset for me! However, for some reason, on a subsequent call to UITableView.layoutSubivews(), the contentOffset was being set BACK its original value (before cell deletion).

Final Solution

So the problem is this: Apple is setting the correct contentOffset to UITableView, but that offset is being set back to the incorrect value on layoutSubviews. However, my saving grace is that, in doing so, scrollViewDidScroll gets called. This gives me the opportunity to intercept the incorrect value and change it to the correct one.

The solution is this:

  • If the final contentOffset of the table view after cell deletion is different from the contentOffset before cell deletion, then save that contentOffset to a variable. This is the correct contentOffset.
  • In scrollViewDidScroll, if the scroll view’s contentOffset does not match the contentOffset we saved, then call setContentOffset and set the offset to the correct value.
  • On willBeginDragging, remove the saved value.

Here’s what it looks like:

Great Success (mostly)

The result was that the jump disappeared! There are still some weird animation bugs going on, but at least the user won’t become disoriented when collapsing a section.

--

--