Drag and drop for NSCollectionView's sections

Hi,

I am building an app, where I have a collection view, which is using decoration views (background for a section), supplementary items (section's header) and of course items.

I would like to provide drag and drop for two kinds of views:
  • sections (they would be moved by dragging header view)

  • items (already implemented)

The problems are in the implementation that would "take" the whole section (with decoration views, items, etc.) - I don't know how can I implement it, should I create an external class as a drag and drop delegate and assign its instance to every NSView which is a header? Or they should be self-managed (but they don't know anything about their superview)?

I have tried to implement nested collection views, where item of root collection view has its own collection view and views, that are replacing all supplementary and decoration views, but I can't there make them working together, for example - I can't select multiple items from different sections as items from previous section are deselected when tapping on items in other one. Also it's tough to make drag'n'drop working between them (especially using dragging rectangle area) and it's the best way to implement it when we're talking about performance.

I appreciate any kind of help/ideas to implement it.

Compositional layouts code


I had a similar issue when IKImageBrowserView was deprecated and I had to move to CollectionView. After checking many different methods, I ended up subclassing CollectionView to override NSDraggingDestination then testing the -collectionViewLayout's -layoutAttributesForDropTargetAtPoint: representedElementCategory and representedElementKind. Super can be called at every step and tested if the result qualifies as a group/section destination so you're not forced to reimplement everything, just make sure to tell the delegate to accept the drop at the end so it can do the "real" work.
Thanks! I have a few questions:
  1. Should header view also conform to NSDraggingDestination? Right now I can't even drag it (it would be the view that decides if I want to move section, not the items inside section, as they're also draggable). Registering draggedType (its my own type) makes only items draggable.

  2. Did the calculations that require from code to recognise type of dragging item and its dropIndexPath impact the performance?

  3. Do You use NSCollectionViewDelegate methods alongside that custom NSDragDestination implementation?

  4. Do You use decoration views (section background view)? I am using them and as I see, they're static views that I can't manage.

  • All work is done in the subclass

  • Not necessarily, because after super you're just checking if you need to "correct" the operation and re-dispatch to the delegate

  • Always, because you're just correcting the location/operation part

  • Only used headers, but the principle is the same.

Just to be sure:
  1. I have to differentiate type of dragging data (section vs item) in collection view's subclass methods related with NSDraggingDestination? Also I can do that with type identifiers, so it won't be a problem.

  2. As I saw the Drag and Drop Programming Guide, I need to override mouseDragged(:) or mouseDown(:) events to make that header view draggable. Did you do that in Your project?

  3. Regarding my last question about decoration views - the problem lies in implementation of the decoration views, layout won't let me access these views when I will want to build NSImageRep for my section, because there isn't any method to acquire them. Only one type of views that I can get (as NSViews) are supplementary views (not counting here NSCollectionViewItem). I am thinking here about reimplementing decorations as supplementary views.

  4. Do You have some part of Your implementation shared somewhere? I would love to see it, as it may greatly reduce my implementation time.

I am sorry for keep asking questions, but knowing these details will make me sure how it has to work and how I should divide code. It's my really first time going that deep into AppKit.

  • If this is your first time in AppKit, you should read all the drag-and-drop resources first, to understand the lifecycle, and how events are separated from the dragging session, are separated from the data/delegate implementation

  • You're not overriding any of the NSResponder events, just the NSDraggingDestination parts: -draggingEntered:, -draggingUpdated:, -draggingEnded:, and -performDragOperation:

  • None of this override deals with the data, just the dragging location and operation. If you detect that the current mouse location for dragging is on a header (see my mention of layout attributes), handle things differently from what the superclass does.

I have wrongly asked the question (or not precisely). You have awesomely explained what I have to do to make collection view both sections and items droppable on it, but I don't know how to make section header draggable and that's why I was speaking about NSDraggingSource, as documentation says it contains a methods that I need to make item draggable (I can take it up and move, then the NSDraggingDestination will handle dropping). And here is my problem - I have to override collectionView's NSDragSource or implement conformity to that for my header view (which is a NSView)? When I am pressing the header view, nothing happens. Only when I was overriding mouseDown(sender:) and mouseDragged(sender:) I was able to read these gestures executed on headers.
Accepted Answer
Dragging the header is much more complex, to the point where you'd probably have to maintain extra state just to identify the session, which would start in the CollectionView's mouse event methods.
The existing delegate methods won't necessarily be able to differentiate, which is another issue: drag item {onto item, before/after item, onto section}, drag section: {before/after section, onto section, onto item?}.
Just as your individual item views don't implement NSDraggingSource (or NSDraggingDestination) to be draggable, your header view wouldn't either.
I did it with steps below:
  • when header view (as it's NSView, it gets mouseDown/mouseDragged) gets information about being clicked, I am passing its IndexPath to collection view (as multiple sections can be dragged in the same time)

  • when header view gets mouseDragged it calls a method in collectionView, which creates NSDraggingItems from given indexPaths (I have to solve additionally how to remove visible collectionview's background from image as section's decoration view has rounded corners) and calls beginDraggingSession(),

  • in draggingUpdated I calculate drop index path (it's easy, as You said, I am comparing minX...maxX to cursor's point)

  • and finally in draggingEnded I use delegate method to execute operations on collectionView and data behind it.

I have to improve a few parts of it (like draggingImage not going back to its position when dragging ends successfully or to hide dragged section), but overall I am satisfied with results.
Thank You for the help!
Drag and drop for NSCollectionView's sections
 
 
Q