Cocoa Dev Quick NotesNSTableView: Drag & Drop and Edit Menu Validation

14 marzo 2010

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;
    }
    
  • Useful links

    NSTableView
    Drag And Drop

Go top