'UICollectionView not calling moveItemAt:To: Data Source method in response to endInteractiveMovement()

I'm having a difficult time getting interactive reordering to work in a UICollectionView embedded in a subclassed UIViewController. There is no custom layout object.

The view controller is set as the delegate and datasource for the collection view, and the collection view is given a long press gesture recognizer as outlined here and here. I've also implemented the datasource methods canMoveItemAt: and moveItemAt:To:

I have another collection view in the view controller which shouldn't reorder, and the VC is the delegate/datasource of both. Thus:

func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {

    if collectionView == cardCollectionView {

        return true
    }
    else {
        return false
    }
}

I can confirm that this datasource method is being called appropriately.

The handler of the long press gesture triggers the appropriate steps, and the cells begin their rearranging behavior during the press. EXCEPT for the response to UIGestureRecognizerState.ended:, which triggers cardCollectionView.endInteractiveMovement(). This should lead to the moveItemAt:To datasource method, but it is never called.

@objc func handleLongGesture(gesture: UILongPressGestureRecognizer) {

        //print(cardCollectionView.delegate)
        switch(gesture.state) {

        case UIGestureRecognizerState.began:
        guard let selectedIndexPath = self.cardCollectionView.indexPathForItem(at: gesture.location(in: self.cardCollectionView)) else {
        break
        }
        cardCollectionView.beginInteractiveMovementForItem(at: selectedIndexPath)
        case UIGestureRecognizerState.changed:
        cardCollectionView.updateInteractiveMovementTargetPosition(gesture.location(in: gesture.view!))
        case UIGestureRecognizerState.ended:
            print("ending")
        cardCollectionView.endInteractiveMovement()
        default:
            print("canceling")
        cardCollectionView.cancelInteractiveMovement()
        }
}  


func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
    print("Starting Index: \(sourceIndexPath.item)")
}

Rather, when the long press ends, the cells just all return to their initial location, and I never get a chance to effect the reordering of the data. What do I need to do to complete this reordering process? Also, when the long press is initiated, the cells bounce around in the reordering animation, but I don't see the "lifted" cell under the press. Is that a separate issue, or are they related? (Code is in Swift 4.0)



Solution 1:[1]

Having a UICollectionViewDropDelegate assigned to a UICollectionView interferes with interactive reordering (iOS 9 style) via the long press gesture handler method.

After tearing down my UIViewController and rebuilding it a line at a time, I isolated the place where interactive reordering failed. The culprit was the one in viewDidLoad where (for other reasons) I assigned the ViewController to be the dropDelegate for the CollectionView:

        self.cardCollectionView.dropDelegate = self

The workaround was to simply implement reordering via the new iOS 11 drag and drop API's (WWDC 2017 session 223, around 32:00)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Steven Hovater