1
votes

I'm trying to implement dragging items from an NSCollectionView (not just dropping things on it).

In my example code, I'm registering the CollectionView from dragging:

collectionView.registerForDraggedTypes([.URL])
collectionView.setDraggingSourceOperationMask(.every, forLocal: false)
collectionView.setDraggingSourceOperationMask(.every, forLocal: true)

Then, I've implemented these methods from the NSCollectionViewDelegate protocol:

func collectionView(_ collectionView: NSCollectionView, canDragItemsAt indexPaths: Set<IndexPath>, with event: NSEvent) -> Bool {
    return true
}

func collectionView(_ collectionView: NSCollectionView, pasteboardWriterForItemAt indexPath: IndexPath) -> NSPasteboardWriting? {    
    return URL(fileURLWithPath: #file) as NSPasteboardWriting
}

func collectionView(_ collectionView: NSCollectionView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forItemsAt indexPaths: Set<IndexPath>) { }

func collectionView(_ collectionView: NSCollectionView, draggingSession session: NSDraggingSession, endedAt screenPoint: NSPoint, dragOperation operation: NSDragOperation) { }

But neither of them is ever even called! Why not?

If I add these two methods:

func collectionView(_ collectionView: NSCollectionView, validateDrop draggingInfo: NSDraggingInfo, proposedIndexPath proposedDropIndexPath: AutoreleasingUnsafeMutablePointer<NSIndexPath>, dropOperation proposedDropOperation: UnsafeMutablePointer<NSCollectionView.DropOperation>) -> NSDragOperation {
    return .move
}

func collectionView(_ collectionView: NSCollectionView, acceptDrop draggingInfo: NSDraggingInfo, indexPath: IndexPath, dropOperation: NSCollectionView.DropOperation) -> Bool {
    return true
}

Then I can successfully drop files from the Desktop into the collection view, but still not the other way around.

What's going on?

Best regards, V.

1
Is "Selectable" of the collection view switched on?Willeke
@Willeke: I just tried it again with isSelectable set to both, false and true, and it doesn't seem to make any difference at all. :(Vogel Vogel

1 Answers

0
votes

Exactly the same here.

It looks like a bug 🐛 (Xcode 9.4.1, Swit 4.1.2).

Just like you mentioned, validateDrop and acceptDrop get called in my app.
But (for example), endedAt does not:

func collectionView(_ collectionView: NSCollectionView,
                    draggingSession session: NSDraggingSession,
                    endedAt screenPoint: NSPoint,
                    dragOperation operation: NSDragOperation) { }

The only workaround I could find (for the above endedAt delegate) was to subclass NSCollectionView (which I'm calling ImageCollectionView in the example below) and implement my own delegate/protocol:

import Cocoa

protocol ImageCollectionViewDelegate {
    func didExitDragging()
}

class ImageCollectionView: NSCollectionView {
    var imageCollectionViewDelegate: ImageCollectionViewDelegate?
}

extension ImageCollectionView {
    override func draggingExited(_ sender: NSDraggingInfo?) {
        super.draggingExited(sender)
        imageCollectionViewDelegate?.didExitDragging()
    }
}

(I had to call it imageCollectionViewDelegate to not conflict with the already existing delegate property.)

Then, in my controller (which I'm calling ImageCollectionViewController):

@IBOutlet internal weak var imageCollectionView: ImageCollectionView! {
    didSet {
        imageCollectionView.imageCollectionViewDelegate = self
    }
}

extension ImageCollectionViewController: ImageCollectionViewDelegate {
    func didExitDragging() {
        highlightDestination(false)
    }
}

That allows me to do stuff when dragging outside the collection.
In this very simple use case, just turning the destination view's highlight on/off:

highlighting

Ideally all this extra code wouldn't be necessary.

I'm also looking for the proper way of handling this. 👍🏻