I had similar requirements except I had a grid of objects that I wanted to rearrange by dragging selected objects to a new location. There are several ways of doing this, including creating a custom object and implementing the NSPasteboardWriting
and NSPasteboardReading
protocols, (and NSCoding
protocols if you will be reading data as NSPasteboardReadingAsKeyedArchive
), but this seems to be overkill for dragging of objects that remain internal to the application.
What I did involves using the NSPasteboardItem
as a wrapper with a custom UTI type (it already implements the NSPasteboardWriting
and NSPasteboardReading
protocols). First declare a custom UTI type:
#define kUTIMyCustomType @“com.mycompany.MyApp.MyCustomType”
This needs to be defined in the ‘com.domain.MyApp’ format otherwise you will get errors of the form: “XXX is not a valid UTI string. Cannot set data for an invalid UTI.” Apple mentions this in their documentation.
Then you must register this custom UTI type in the view in which your dragging will occur. This can be done at runtime, and does not require any .plist additions. In your view's init method add the following:
[self registerForDraggedTypes:[NSArray arrayWithObjects:(NSString *)kUTIMyCustomType, nil]];
Now, make sure that the delegate is set for this view, and the delegate object implements the required NSDraggingSource
and NSDraggingDestination
protocol methods. This will allow you to avoid breaking the MVC design pattern, by allowing the designated controller object to handle placing the data on the pasteboard which will likely involve querying model data (i.e., indexes).
Specifically, for placing on the dragging pasteboard the indexes of objects to be moved when dragging begins as NSPasteboardItem
wrappers of your index data:
- (void) draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint
{
NSPasteboard * pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
[pboard clearContents];
NSMutableArray * selectedIndexes = [NSMutableArray array];
for (MyModel * myModel in [self selectedObjects])
{
NSPasteboardItem * pasteboardItem = [[[NSPasteboardItem alloc] init] autorelease];
[pasteboardItem setString:[NSString stringWithFormat:@"%@", [myModel index]]
forType:kUTIMyCustomType];
[selectedIndexes addObject:pasteboardItem];
}
[pboard writeObjects:selectedIndexes];
}
And when the dragging operation completes, to read the dragged index NSPasteboardItem
data:
- (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
{
NSPasteboard * pasteboard = [sender draggingPasteboard];
NSArray * classArray = [NSArray arrayWithObject:[NSPasteboardItem class]];
NSArray * items = [pasteboard readObjectsForClasses:classArray options:[NSDictionary dictionary]];
if (items == nil)
return NO;
NSMutableArray * indexes = [NSMutableArray array];
for (NSPasteboardItem * item in items)
[indexes addObject:[NSNumber numberWithInteger:[[item stringForType:kUTIMyCustomType] integerValue]]];
return YES;
}