Using a UITableViewDropCoordinator to animate drops via the dropDelegate with a diffable data source

573 views Asked by At

In iOS 11 Apple introduced native drag and drop to TableViews which provided specifc animations for common drag and drop interactions. Assuming you had returned the correct UIDropProposal it would easily animate a reordering drop in a table view

func tableView(_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator) {
  // usual code to handle drop, get dragItem, etc
  // ....

  // update the dataModel to reflect the change
  self?.model.updateDataSourceForDrag(from: sourceIndexPath, to: destinationIndexPath)

 // perform the drop animation with the drop coordinator
 coordinator.drop(dragItem, toRowAt: destinationIndexPath)
}

and this would nicely animate the insertion of the dropped item into the tableView into the gap it was hovering over.

Fast forward to iOS 13+ and the use of diffable data source and I can find no reference to using the dropCoordinator with snapshots in the Apple documentation, and there has been no update to the guides, tutorials, or WWDC videos to show how to combine the two sets of APIs.

The drop controller will correctly "manage" the tableView during the drag operation and move the cells around to show the gap where dragged cell would drop, but it won't animate the actual drop with coordinator.drop(dragItem, toRowAt: destinationIndexPath).

The workaround I have currently is to manually update and then apply the snapshot:

DispatchQueue.main.async {
  var snapshot = self?.dataSource.snapshot()
  if destinationIndexPath.row > sourceIndexPath.row {
     snapshot?.moveItem((self?.dataSource.itemIdentifier(for: sourceIndexPath))!, afterItem: (self?.dataSource.itemIdentifier(for: destinationIndexPath))!)
  } else {
     snapshot?.moveItem((self?.dataSource.itemIdentifier(for: sourceIndexPath))!, beforeItem: (self?.dataSource.itemIdentifier(for: destinationIndexPath))!)
  }
  self?.dataSource.apply(snapshot!, animatingDifferences: false)
}

This works to a degree, but it doesn't integrate with the drop controller animation. So I can get the animation providing drop gaps in the tableView while I drag, but as soon as I drop it animates the cell move from its original indexPath rather than from the animated dropped items (as I'd expect for this workaround) which is all rather clunky.

That the dropCoordinator can adapt the tableView to animate the drag suggests it should be able to do animate the drop too, but I can find no way to achieve this.

Any experience with this would be gratefully received (before I give up and revert the code back to the older UITableViewDataSource methods).

1

There are 1 answers

0
flanker On

The drop animation is now working as expected on code that hasn't been modified for months.

There must have been a bug in iOS14 and early versions of iOS15 that was fixed in one of the recent updates.

All that wasted time and effort. At times we really should have more faith in our own code.