In iOS 9, UICollectionView learned to interactively re-order cells. Let's take a look.

Before we dive in, one quick note: If we just need re-orderable cells, adopting this functionality can be incredibly simple:

override func collectionView(collectionView: UICollectionView, moveItemAtIndexPath source: NSIndexPath, toIndexPath destination: NSIndexPath) {
  let person = crew.removeAtIndex(source.item)
  crew.insert(person, atIndex: destination.item)
}

UICollectionViewController's new property called installsStandardGestureForInteractiveMovement defaults to true, so once we implement delegate function above, we're good to go.

To customize things further, we'll need to manually call UICollectionView's new interactive movement functions. Let's look at a very rough idea of how we might make the picked up cell "hover" and the others "wiggle" like iOS's home screen.

func longPressed(gesture: UILongPressGestureRecognizer) {
  let location = gesture.locationInView(collectionView)
  movingIndexPath = collectionView.indexPathForItemAtPoint(location)

  switch(gesture.state) {
  case .Began:
    guard let indexPath = movingIndexPath else { break }
    setEditing(true, animated: true)
    collectionView.beginInteractiveMovementForItemAtIndexPath(indexPath)
    pickedUpCell()?.stopWiggling()
    animatePickingUpCell(pickedUpCell())
  case .Changed:
    collectionView.updateInteractiveMovementTargetPosition(location)
  case .Ended:
    collectionView.endInteractiveMovement()
    animatePuttingDownCell(pickedUpCell())
    movingIndexPath = nil
  default:
    collectionView.cancelInteractiveMovement()
    animatePuttingDownCell(pickedUpCell())
    movingIndexPath = nil
  }
}

override func setEditing(editing: Bool, animated: Bool) {
  super.setEditing(editing, animated: true)
  startWigglingAllVisibleCells()
}

We'll also start/stop wiggling when we dequeue cells. Lastly, we'll apply the same changes that are in animatePickingUpCell to the cell's layout attributes. To do this we can subclass UICollectionViewFlowLayout and override layoutAttributesForInteractivelyMovingItemAtIndexPath.

After all that's done, this is the outcome:

Here's a direct link to this example video, just in case.

Download the project at j.mp/bite104 to see a complete working example.

Update on March 22nd, 2019: Thanks to reader Le Zeng, the project download now supports Swift 4.2. Thanks so much!