5

I have a UITableView which under goes a batch update when the state of app changes. The change can effect a number of rows/sections or only one section (but 1 or more rows).

I've noticed that when under going a single section update (which may effect a number of rows in the section), the section headers for the other sections "bounce" or are "moved" from their previous position and then animated back into place.

A picture paints a thousand words...

Video Capture

These sections are not part of the batch update.

The UITableView/Controller is setup through a story board targeting iOS 11. The UITableView is making use of the "Estimated" row and section heights and the cells are laid out using auto layout.

Xcode storyboard properties

The UITableViewController makes use of a larger data model which managers the state and state change operations. The API ensures that the required updates are calculated based on the required state of the model (i.e. deletes and updates relate to the "previous" state, inserts related to the "new" state)

The last step is to apply the calculated changes to the table view through a batch update process...

tableView.beginUpdates()

// Calculate the rows/sections which need to be changed

if let ops = operation.sections[.delete], ops.count > 0 {
    tableView.deleteSections(ops, with: deleteSectionAnimation)
}
if let ops = operation.sections[.update], ops.count > 0 {
    tableView.reloadSections(ops, with: reloadSectionAnimation)
}
if let ops = operation.sections[.insert], ops.count > 0 {
    tableView.insertSections(ops, with: insertSectionAnimation)
}

if let ops = operation.rows[.delete], ops.count > 0 {
    tableView.deleteRows(at: ops, with: deleteRowAnimation)
}
if let ops = operation.rows[.update], ops.count > 0 {
    tableView.reloadRows(at: ops, with: reloadRowAnimation)
}
if let ops = operation.rows[.insert], ops.count > 0 {
    tableView.insertRows(at: ops, with: insertRowAnimation)
}
tableView.endUpdates()

tableView.reloadData()
tableView.layoutIfNeeded()

The row and section animation properties (i.e. deleteSectionAnimation) are set to automatic. I've tried setting the section animations to none without any differences

I've tried removing the reloadData and layoutIfNeeded in varying combinations and while they create other issues, they don't seem to come close to resolving this issue

One thing I've noticed, is this only seems to occur when the updates occurring to the UITableView are been done off screen

I've spend some time trying to find a solution to the issue (like not calling reloadData) but can't find something which works.

Is there some other property, function I should look at or is the order in which the updates are been executed wrong?

Notes

  • This is a "static" table, setup through storyboards
  • The rows and sections are been added/removed during state changes, which seems to trigger issues/changes in the scroll view
  • While disabling animation (with UIView.setAnimationsEnabled) does seem to fix the issue, it's an undesirable solution, as it removes the animations for all updates (on or off screen)
  • The issue only occurs when the scroll view is NOT at the top of the view, updates/animations occurring when the table view is scrolled all the way to the top have no issues
  • I've tried using indexPathsForVisibleRows to determine if the updates are occurring on or off the screen, and disable the animation using UIView.setAnimationsEnabled but it doesn't solve the issue if the update occurs on the screen while the table view is not scrolled to the top of the scroll view
Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • have you tried setting `UIView.setAnimationsEnabled` to `false` ? and then set it back to `true after you're done updating? Otherwise if you've already tried this, can you try creating a small little project that shows this issue? – TNguyen Nov 08 '17 at 14:34
  • @TNguyen I've tried using `UIView.setAnimationsEnabled` while it does correct the issue, it obviously stops all the other animations I want. I've been looking at a solution to use `UIView.setAnimationsEnabled` when the updates are off screen, but because the state management is insanely complex, any time an update also occurs with in the visible area, when the table view is not at the top of the scroll view, I get the same issues as described above – MadProgrammer Nov 08 '17 at 22:46
  • Not sure if this will work since I've never used it before but after reading the docs it could be useful, maybe try surrounding it in `UIView.performWithoutAnimation` – TNguyen Nov 08 '17 at 23:56
  • @TNguyen I'm pretty sure that it's just a convince for `setAnimationEnabled`. Unfortunately, disabling the animations really isn't an option (as I need them for when the updates occur on screen). I've implemented a version [this solution](https://stackoverflow.com/questions/27996438/jerky-scrolling-after-updating-uitableviewcell-in-place-with-uitableviewautomati) and it's solved the issue of the scroll view bouncing about the place during updates, but the section headers still "bounce"/"move" – MadProgrammer Nov 09 '17 at 00:14
  • @TNguyen The overriding issues seems to be with the use of auto layout and estimated cell/section heights – MadProgrammer Nov 09 '17 at 00:15
  • I was having this same exact issue. I figured it out though. For me, the issue was that the other cells (not the section header) did not have the correct estimated heights. – Gabriel Pires Apr 06 '18 at 00:39
  • @GabrielPires I think (part of the larger issue) is that almost none of the cells have a uniform height – MadProgrammer Apr 17 '18 at 22:51
  • @MadProgrammer I do something very similar to the answer in this SO post (https://stackoverflow.com/questions/41342777/uitableview-with-self-sizing-cells-becomes-jerky-when-calling-deleterowsatindexp) in order to get the exact estimated height. I save the heights of the cell from willDisplay into a dictionary where the key is the indexPath and the value is the height. Then in estimatedHeightForRowAt, I grab the saved heights. I'm not sure how ethical it is to do it like this, but it has worked for me across multiple apps – Gabriel Pires Apr 18 '18 at 01:41
  • @GabrielPires I was doing something similar, be it screw a bunch of things up - probably not doing it right ... also support iOS 9 makes it difficult, thanks though - I'll take another look at it – MadProgrammer Apr 18 '18 at 01:49
  • @MadProgrammer Did you ever find the solution? I am currently having the same issue. – akaakoz Aug 11 '23 at 01:14

0 Answers0