Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSCollectionView Drag Drop example

Tags:

macos

cocoa

I am trying to implement drag drop in NSCollectionView which will allow to re arrange cells in view. I have set the delegate and implemented below methods :

-(BOOL)collectionView:(NSCollectionView *)collectionView writeItemsAtIndexes:(NSIndexSet *)indexes toPasteboard:(NSPasteboard *)pasteboard {
    NSLog(@"Write Items at indexes : %@", indexes);
    return YES;
}

- (BOOL)collectionView:(NSCollectionView *)collectionView canDragItemsAtIndexes:(NSIndexSet *)indexes withEvent:(NSEvent *)event {
    NSLog(@"Can Drag");
    return YES;
}

- (BOOL)collectionView:(NSCollectionView *)collectionView acceptDrop:(id<NSDraggingInfo>)draggingInfo index:(NSInteger)index dropOperation:(NSCollectionViewDropOperation)dropOperation {
    NSLog(@"Accept Drop");
    return YES;
}

-(NSDragOperation)collectionView:(NSCollectionView *)collectionView validateDrop:(id<NSDraggingInfo>)draggingInfo proposedIndex:(NSInteger *)proposedDropIndex dropOperation:(NSCollectionViewDropOperation *)proposedDropOperation {
    NSLog(@"Validate Drop");
    return NSDragOperationMove;
}

I am not sure how to take this further. With this I can see that now I can drag around the individual Collection Item but how can I make the Drop ?

like image 512
user88975 Avatar asked Apr 25 '14 07:04

user88975


Video Answer


2 Answers

You have only implemented the delegate methods but there s no logic in some of the methods. For example to drag around a Collection Item I would add below logic :

-(BOOL)collectionView:(NSCollectionView *)collectionView writeItemsAtIndexes:(NSIndexSet *)indexes toPasteboard:(NSPasteboard *)pasteboard {
    NSData *indexData = [NSKeyedArchiver archivedDataWithRootObject:indexes];
    [pasteboard setDraggedTypes:@["my_drag_type_id"]];
    [pasteboard setData:indexData forType"@"my_drag_type_id"];
    // Here we temporarily store the index of the Cell, 
    // being dragged to pasteboard.
    return YES;
}

- (BOOL)collectionView:(NSCollectionView *)collectionView acceptDrop:(id<NSDraggingInfo>)draggingInfo index:(NSInteger)index dropOperation:(NSCollectionViewDropOperation)dropOperation {
    NSPasteboard *pBoard = [draggingInfo draggingPasteboard];
    NSData *indexData = [pBoard dataForType:@"my_drag_type_id"];
    NSIndexSet *indexes = [NSKeyedUnarchiver unarchiveObjectWithData:indexData];
    NSInteger draggedCell = [indexes firstIndex];
    // Now we know the Original Index (draggedCell) and the 
    // index of destination (index). Simply swap them in the collection view array.
    return YES;
}

You also need to register the collection view to drag type in awakefromnib as

[_myCollectionView registerForDraggedTypes:@[@"my_drag_type_id"]];

And make sure that you have set the collection view as selectable.

like image 98
GoodSp33d Avatar answered Nov 15 '22 14:11

GoodSp33d


In addition to what GoodSp33d mentions above, you're also missing the validate delegate function which is required to accept drops. In Swift this is:

func collectionView(_ collectionView: NSCollectionView, validateDrop draggingInfo: NSDraggingInfo, proposedIndexPath proposedDropIndexPath: AutoreleasingUnsafeMutablePointer<NSIndexPath>, dropOperation proposedDropOperation: UnsafeMutablePointer<NSCollectionViewDropOperation>) -> NSDragOperation

Note the return value, NSDragOperation. This method should contain code that determines precisely what kind of drag operation is being attempted and returns this value. Returning the wrong thing can lead to some pretty annoying bugs.

Further note that in order to support this kind of operation, the collection view layout class you are using must also support drag and drop. Flow layout should do this out-of-the-box, but if you're using a custom layout you may need to adapt it to support drag-and-drop so that the collection view can detect valid drop targets and determine a suitable index path for them.

like image 40
Ash Avatar answered Nov 15 '22 15:11

Ash