I am using an NSFetchedResultsController for my Data Source.
When I reorder a cell and it moves up or down to a position that is on screen or just slightly off screen, the cell moves to it's new position with an animation.
However, when the row is moving to a new position which is well off screen, it just gets moved without any animation.
Ideally, I would like the row in those cases to animate down or up until it's off screen. Is there any way to achieve this without implementing a custom method?
The case I am using here is the .move in the following delegate call:
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
tableView.insertRows(at: [newIndexPath!], with: UITableViewRowAnimation.none)
case .delete:
tableView.deleteRows(at: [indexPath!], with: UITableViewRowAnimation.none)
case .update:
tableView.reloadRows(at: [indexPath!], with: UITableViewRowAnimation.none)
case .move:
tableView.moveRow(at: indexPath!, to: newIndexPath!)
}
}
From the docs, UIKit
will animate move operations for all cells, however it is happening so fast that it's not very visual.
So, you can actually get the desired effect by two move(at:,to:)
calls with performBatchUpdates
as follows:
guard indexPath != newIndexPath, let paths = tableView.indexPathsForVisibleRows else { return }
if paths.contains(newIndexPath!) {
tableView.moveRow(at: indexPath!, to: newIndexPath!)
} else {
tableView.performBatchUpdates({
let index = indexPath < newIndexPath ? (paths.count - 1) : 2
tableView.moveRow(at:indexPath!, to: paths[index])
tableView.moveRow(at: paths[index], to: newIndexPath!)
})
}
Note that to achieve upward scrolling animation, you have to set the paths
index to be 2 as the midpoint for moving upward.