NSPasteboard type for NSManagedObject
I need to drag a reference to an NSManagedObject b开发者_开发知识库etween two table views of my application. What's the preferred NSPasteboard type to store a reference to an NSManagedObject?
My current solution is to store the URIRepresentation of the object's NSManagedObjectID in a NSPasteboardTypeString. I suspect there's a more elegant solution out there.
There is no standard type for all model objects since your model objects and how they're handled are unique to your application. If there was one pasteboard type for all then there'd be no telling them apart. Your own custom object should have its own drag type.
Just use a string that makes sense (maybe a #define so you can find it with auto-complete in Xcode) like "MyObjectPboardType" that resolves to "com.yourcompany.yourapp.yourobjecttype".
Use NSPasteboard's -declareTypes:owner: to declare your new type, then use -setString:forType: or one of the other -set?:forType: methods to set the information for your object's type. In your case, the use of the object ID is a perfectly acceptable identifier. Just remember managed objects' object IDs change when they're new versus persisted.
If you are dragging within tables in the same application you might as well put in pasteboard the rowIndexes (indexPaths in case you are dragging from an outlineView) of the objects in the tableView (outlineView). This might as well spare you from some unneeded CoreData access if the dataSource of the tableViews are NSArrayController (NSTreeController for outlineView). You can then easily retrieve the dragged objects when accepting the drop since the ‘info‘ object passed to both methods ‘tableView:validateDrop:proposedRow: proposedDropOperation:‘ and to ‘tableView:acceptDrop:row:dropOperation:‘ will have a reference to the tableView originating the drag under ‘draggingSource‘ key path.
Here's a simple implementation:
extern NSString *const kMyLocalDragType = @"com.whatever.localDragType";
@implementation MyArrayControllerDataSource
.
.
.
#pragma mark - NSTableViewDataSource (Drag & Drop)
+ (NSArray *)dragTypes {
// convenience method returning all class's supported dragTypes
return @[kMyLocalDragType];
}
- (BOOL)tableView:(NSTableView *)tableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard {
[pboard declareTypes:[[self class] dragTypes] owner:self];
for (NSString *aDragType in [[self class] dragTypes]) {
if (aDragType == kMyLocalDragType) {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes]; // we are supporting drag&drop of multiple items selected
[pboard setData:data forType:aDragType];
}
.
. // logic for other dragTypes
.
}
return YES;
}
- (NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id<NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)dropOperation {
NSArray *dragTypes = [info draggingPasteboard] types];
for (id aDragType in dragTypes) {
if (aDragType == kMyLocalDragType) {
return NSDragOperationCopy;
}
}
.
.// Other logic for accepting drops/affect drop operation
.
}
- (BOOL)tableView:(NSTableView *)tableView acceptDrop:(id<NSDraggingInfo>)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)dropOperation {
if ([info draggingPasteboard] types] containsObject:kMyLocalDragType]) {
// Retrieve the index set from the pasteboard:
NSData *data = [[info draggingPasteboard] dataForType:kMyLocalDragType];
NSIndexSet *rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSArray *droppedObjects = [self retrieveFromTableView:tableView objectsAtRows:rowIndexes];
// droppedObjects contains dragged and dropped objects, do what you
// need to do with them, then add them to this dataSource:
[self.content insertObjects:droppedObjects];
[tableView reloadData];
[tableView deselectAll:nil];
return YES;
}
.
. // other logic for accepting drops of other dragTypes supported.
.
}
#pragma mark - Helpers
- (NSArray <NSManagedObject *> *)retrieveFromTableView:(NSTableView *)tableView objectsAtRowIndexes:(NSIndexSet *)rowIndexes {
id dataSource = [tableView dataSource];
if ([dataSource respondsToSelector:@selector(content)]) {
if ([dataSource.content respondsToSelector:@selector(objectsAtIndexes:)]) {
return [datasource content] objectsAtIndexes:rowIndexes];
}
}
return @[]; //We return an empty array in case introspection check failed
}
精彩评论