All methods are updated to Mac OS 10.6.
Drag & Drop
Follow this steps to use drag & drop in NSTableView:
- Create a custom view controller class MyTableViewController based on NSViewController
- Add a new XIB based on an empty view and assign the newly added class to its File’s Owner
- Drop in a NSTableView and assign its dataSource outlet to File’s Owner
- Use Core Data for populating the table
- Define a custom pasteboard type (only if you aren’t using standard types):
NSString *MyCustomPBoardType = @"MyCustomBoardType";
- In your view controller’s awakeFromNib method add the following lines:
NSArray *dragTypes = [NSArray arrayWithObjects: MyCustomPBoardType,nil]; [myTableView registerForDraggedTypes:dragTypes]; [myTableView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
- Implement the tableView:writeRowsWithIndexes:toPasteboard: method to handle dragging and tableView:validateDrop:proposedRow:proposedDropOperation: and tableView:acceptDrop:row:dropOperation: to handle dropping:
- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard { NSArray *selectedObjects = [myArrayController selectedObjects]; [pboard declareTypes:[NSArray arrayWithObjects:MyCustomPBoardType,nil] owner:self]; for( NSManagedObject *object in selectedObjects ) [pboard writeObjects:[NSArray arrayWithObject:[[object objectID] URIRepresentation]]]; return YES; } - (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id )info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)op { if ([info draggingSource] == tv) { [tv setDropRow:row dropOperation:NSTableViewDropAbove]; return NSDragOperationNone; } else { return NSDragOperationCopy; } return NSDragOperationNone; } - (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id )info row:(int)row dropOperation:(NSTableViewDropOperation)operation { BOOL acceptDrop = NO; if( [info draggingSource] == aTableView ) return acceptDrop; // it allows only drops from external sources NSArray *classes = [[NSArray alloc] initWithObjects:[NSURL class], nil]; NSDictionary *options = [NSDictionary dictionary]; NSArray *copiedItems = [[info draggingPasteboard] readObjectsForClasses:classes options:options]; if( !copiedItems ) return acceptDrop; // do whatever you like with copied items NSArray *currentEvents = [objectsArrayController selectedObjects]; for( NSManagedObject *event in currentEvents ) { NSSet *attendants = [event valueForKey:@"attendants"]; NSMutableSet *newAttendants = [[[NSMutableSet alloc] initWithCapacity:0] autorelease]; [newAttendants setSet:attendants]; for( NSURL *copiedObject in copiedItems ) [newAttendants addObject:[self.managedObjectContext objectWithID:[[self.managedObjectContext persistentStoreCoordinator] managedObjectIDForURIRepresentation:copiedObject]]]; [event setValue:newAttendants forKey:@"attendants"]; acceptDrop = YES; } return acceptDrop; }
Edit Menu Validation
The simplest way to validate cut:, copy: and paste: actions for rows in a table without subclassing NSTableView is to delegate the view controller.
- The responder chain will ask first the NSTableView object and then will look for the next responder. So it’s simple to set the view controller as the next responder in your awakeFromNib method:
[myTableView setNextResponder:self];
- Now implement the request actions in your view controller:
- (IBAction)copy:(id)sender { ... } - (IBAction)cut:(id)sender { ... } - (IBAction)paste:(id)sender { ... } // and whatever you like
- Validate the interface:
- (BOOL)validateUserInterfaceItem:(id )anItem { if ([anItem action] == @selector(copy:)) { return [self canCutCopyDeleteOrDuplicate]; } return YES; }