I've got a little demo on how to use NSOutlineView with drag and drop on my downloads page: http://davedelong.com/downloads It should be pretty applicable to NSTableView, since NSOutlineView is a subclass of NSTableView.
Here is a 5 minute distillation of what I've learned after about a month of reading documentation. There are three "basic" NSTableView delegate methods used when dragging and dropping stuff from NSTableViews. They are:
- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id <NSDraggingInfo>)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)operation;
- (NSDragOperation)tableView:(NSTableView *)aTableView validateDrop:(id < NSDraggingInfo >)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)operation;
- (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard;
The first one, tableView:acceptDrop:row:dropOperation:
is used when someone has dragged something ONTO your NSTableView. It's pretty straight-forward. All the information about the drop (including what's being dropped) is found in [info draggingPasteboard]
. Return YES
if the drop was successful, and NO
otherwise.
tableView:validateDrop:proposedRow:proposedDropOperation:
is used when someone WANTS to drop something onto your NSTableView. The originator of the drop, at this point, is unimportant. All that matters is that they're hovering over you, and you have to say what they can do. If, for example, they're hovering over a certain row (the 'proposedRow'), and you don't want to allow stuff to be dropped on that row, then return NSDragOperationNone
. Or maybe you want to copy what's in the information (return NSDragOperationCopy
). There's a whole bunch of different kinds of operations you can signify. Use the one that is appropriate for your needs. (These return values will adjust the cursor accordingly. So if you return NSDragOperationCopy, then the cursor will get the little green +
circle.)
The final method, tableView:writeRowsWithIndexes:toPasteboard:
is called when the user has selected some rows in your NSTableView and has begun to drag them. You now have to provide the dragging pasteboard with the information that corresponds to those rows, so it can be dropped elsewhere. Here's a simplified example of how I've used this method to provide NSManagedObjects to the pasteboard:
- (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard {
[pboard declareTypes:[NSArray arrayWithObject:kMyCustomDragType] owner:self];
//get an array of URIs for the selected objects
NSMutableArray * rows = [NSMutableArray array];
NSArray * selectedObjects = [arrayOfManagedObjects objectsAtIndexes:rowIndexes];
for (NSManagedObject * o in selectedObjects) {
[rows addObject:[[o objectID] URIRepresentation]];
}
NSData * encodedIDs = [NSKeyedArchiver archivedDataWithRootObject:rows];
[pboard setData:encodedIDs forType:kMyCustomDragType];
return YES;
}
The idea behind this is that I'm encoding the URIRepresentation of each selected NSManagedObject's objectID and putting that on the pasteboard. I'm putting this data on the draggingPasteboard under the type "kMyCustomDragType" (an NSString), which means that only objects that have indicated that they accept drops of type kMyCustomDragType will be able to receive this drop. I finally return YES
to indicate that I have successfully written data to the pasteboard. (Return NO
if there was a failure)
If you can get this to work, then you've probably got 90% of all your drag and drop functionality that you'll need. The other 10% would come from stranger requirements. As always, the documentation will be your best friend.