Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preventing moving UICollectionViewCell by its center when dragging

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.

like image 309
thierryb Avatar asked Sep 19 '25 08:09

thierryb


2 Answers

(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()
    }
}
like image 134
Spencer Curtis Avatar answered Sep 21 '25 16:09

Spencer Curtis


If your collection view is only scrolling in once direction then the easiest way to achieve this is to simple lock the axis which isnt scrolling to something hardcoded, this means your cell will only move in the axis that you can scroll. Here is the code, see the changed case...

@objc func handleLongGesture(gesture: UILongPressGestureRecognizer) {
    switch gesture.state {
    case .began:
        guard let selectedIndexPath = self.collectionView
            .indexPathForItem(at: gesture
                .location(in: self.collectionView)) else { break }
        collectionView.beginInteractiveMovementForItem(at: selectedIndexPath)
    case .changed:
        var gesturePosition = gesture.location(in: gesture.view!)
        gesturePosition.x = (self.collectionView.frame.width / 2) - 20
        collectionView.updateInteractiveMovementTargetPosition(gesturePosition)
    case .ended:
        collectionView.endInteractiveMovement()
    default:
        collectionView.cancelInteractiveMovement()
    }
}
like image 20
Stoff81 Avatar answered Sep 21 '25 14:09

Stoff81