How can I disable cell dragging between UICollectionView sections?

1.7k views Asked by At

I have two sections in my UICollectionView. I would like to be able to drag cells within each section but not between them.

I am using a long press gesture recognizer to animate the cell drag movement so I could check that the drop location is not in a different section. Is there a way to determine a section's frame?

Or is there a simpler way?

3

There are 3 answers

0
Tony_Kara On

So i ran into an issue such as the author described above, could not find a way to solve it on the Internet, eventually tried the below and it worked.

   func collectionView(
    _ collectionView: UICollectionView,
    dropSessionDidUpdate session: UIDropSession,
    withDestinationIndexPath destinationIndexPath: IndexPath?
) -> UICollectionViewDropProposal {
    let sectionOfDraggableCell = 1
    guard var destinationIndexPath = destinationIndexPath,
          destinationIndexPath.section == sectionOfDraggableCell else {
        return UICollectionViewDropProposal(operation: .forbidden)
    }

    return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
}

good luck.

2
Sergey_VC On

There is a very simple solution. I have tried it in tableView - but suppose it works fine with Collections also. Though, it's not "a clean and swifty one" - so I would suggest to use it as a temporary instrument:

Inside moveRowAt function you check, if section in sourceIndexPath matches a section in destinationIndexPath. If sections are the same - you let the method to go on. If sections in are different - the function will stop.

This is how it looks like in code:

func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let sourceSection = sourceIndexPath.section
let destinationSection = destinationIndexPath.section
        
if sourceSection == destinationSection {
        
// Here you can put your code, e.x:
//
//let removedItem = yourArray.remove(at: sourceIndexPath.row)
//yourArray.insert(removedItem, at: destinationIndexPath.row)
} else {          
//print ("not this time")
}
}
0
Tonespy On

In the UICollectionViewDropDelegate, this protocol func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal can help with it.

Check the sample below for how I prevent an item being dragged from one section to the other section:

In UICollectionViewDragDelegate, we use the itemsForBeginning function to pass information about the object. You can see that, I passed the index and item to the localObject

func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
    let item = sectionViewModel[indexPath.section].items[indexPath.row]
    let itemProvider = NSItemProvider(object: item.title as NSString)
    let dragItem = UIDragItem(itemProvider: itemProvider)
    dragItem.localObject = (item, indexPath)
    return [dragItem]
}

In UICollectionViewDropDelegate, I did this:

func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
    if let object = session.items.first?.localObject as? (Item, IndexPath), object.0.status, let destinationIndexPath = destinationIndexPath, object.1.section == destinationIndexPath.section {
        let itemAtDestination = sectionViewModel[destinationIndexPath.section].items[destinationIndexPath.row]
        if itemAtDestination.status {
            return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
        }
    }
    return UICollectionViewDropProposal(operation: .forbidden)
}

According to Apple:

While the user is dragging content, the collection view calls this method repeatedly to determine how you would handle the drop if it occurred at the specified location. The collection view provides visual feedback to the user based on your proposal. In your implementation of this method, create a UICollectionViewDropProposal object and use it to convey your intentions. Because this method is called repeatedly while the user drags over the table view, your implementation should return as quickly as possible.

What I Did: I've a couple of restrictions to cover:

  • Prevent item.status == true from going to items within the same section
  • Prevent item from going to other sections

GIF Drag Sample