Preventing moving UICollectionViewCell by its center when dragging

ios9, uicollectionview, uicollectionviewcell

Solution

(Written in Swift 3.1)

For the `targetPosition` parameter of the `updateInteractiveMovementTargetPosition` function, instead of using the gesture recognizer's location directly like this...

var location = recognizer.location(in: collectionView)            
collectionView.updateInteractiveMovementTargetPosition(location)

... I created a function that takes the center of the cell to be dragged (The location that the collectionView `updateInteractiveMovementTargetPosition` would use, and then takes the location of the gesture recognizer's touch in the cell, and subtracts that from the center of the cell.

func offsetOfTouchFrom(recognizer: UIGestureRecognizer, inCell cell: UICollectionViewCell) -> CGPoint {

    let locationOfTouchInCell = recognizer.location(in: cell)

    let cellCenterX = cell.frame.width / 2
    let cellCenterY = cell.frame.height / 2

    let cellCenter = CGPoint(x: cellCenterX, y: cellCenterY)

    var offSetPoint = CGPoint.zero

    offSetPoint.y = cellCenter.y - locationOfTouchInCell.y
    offSetPoint.x = cellCenter.x - locationOfTouchInCell.x

    return offSetPoint

}

I have a simple `var offsetForCollectionViewCellBeingMoved: CGPoint = .zero` in my view controller that will store that offset so function above doesn't need to be called every time the gesture recognizer's location changes.

So the target of my gesture recognizer would look like this:

func collectionViewLongPressGestureRecognizerWasTriggered(recognizer: UILongPressGestureRecognizer) {

    guard let indexPath = collectionView.indexPathForItem(at: recognizer.location(in: self.collectionView)),
        let cell = collectionView.cellForItem(at: indexPath), indexPath.item != 0 else { return }

    switch recognizer.state {

    case .began:

        collectionView.beginInteractiveMovementForItem(at: indexPath)

        // This is the class variable I mentioned above
        offsetForCollectionViewCellBeingMoved = offsetOfTouchFrom(recognizer: recognizer, inCell: cell)

        // This is the vanilla location of the touch that alone would make the cell's center snap to your touch location
        var location = recognizer.location(in: collectionView)

        /* These two lines add the offset calculated a couple lines up to 
        the normal location to make it so you can drag from any part of the 
        cell and have it stay where your finger is. */

        location.x += offsetForCollectionViewCellBeingMoved.x
        location.y += offsetForCollectionViewCellBeingMoved.y

        collectionView.updateInteractiveMovementTargetPosition(location)

    case .changed:

        var location = recognizer.location(in: collectionView)

        location.x += offsetForCollectionViewCellBeingMoved.x
        location.y += offsetForCollectionViewCellBeingMoved.y

        collectionView.updateInteractiveMovementTargetPosition(location)

    case .ended:
        collectionView.endInteractiveMovement()

    default:
        collectionView.cancelInteractiveMovement()
    }
}

Problem

I am able to reorder my UICollectionViewCells on iOS 9 by dragging it using a gesture recognizer and simplementing newly iOS 9 support for reordering. ``` public func beginInteractiveMovementForItemAtIndexPath(indexPath: NSIndexPath) -> Bool // returns NO if reordering was prevented from beginning - otherwise YES public func updateInteractiveMovementTargetPosition(targetPosition: CGPoint) public func endInteractiveMovement() public func cancelInteractiveMovement() ``` I noticed that when I start dragging a cell, its center changes to be the touch location, I don't like that. I would like to be able to drag my cell by it's corner if I want. Do you know how to do this? Thanks a lot.

Original source