Cocoa – Drag and drop from within a NSOutlineView

cocoadrag and dropnsoutlineview

I'm trying to figure out how to implement drag and drop from within a NSOutlineView to itself.

For example, I want the user to be able to reorder and nest and re-nest the items in the NSOutlineView, but I don't need to be able to drag items from any other source (like files or from another view).

All the examples I can find deal with dragging item into the NSOutlineView, not just within itself and seem overly complex. I assume this is possible.

Can someone point be to some docs that deal with just this case? Maybe I'm just missing the obvious.

Best Answer

I have found the documentation of this to be somewhat unclear, and among other things the new ways to do it were added in Lion.

Assuming you need this for 10.7 or higher, then this could be a skeleton for you implementation:

In your data source/delegate class, implement:

// A method to register the view for dragged items from itself. 
// Call it during initialization
-(void) enableDragNDrop
{
    [self.outlineView registerForDraggedTypes: [NSArray arrayWithObject: @"DragID"]];
}


- (id <NSPasteboardWriting>)outlineView:(NSOutlineView *)outlineView pasteboardWriterForItem:(id)item 
{
    // No dragging if <some condition isn't met>

    if ( dontDrag )  {
        return nil;
    }

    // With all the blocking conditions out of the way,
    // return a pasteboard writer.

    // Implement a uniqueStringRepresentation method (or similar)
    // in the classes that you use for your items.
    // If you have other ways to get a unique string representation
    // of your item, you can just use that.
    NSString *stringRep = [item uniqueStringRepresentation];

    NSPasteboardItem *pboardItem = [[NSPasteboardItem alloc] init];

    [pboardItem setString:stringRep forType: @"DragID"];

    return pboardItem;
}


- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
{
    BOOL dragOK = // Figure out if your dragged item(s) can be dropped
                  // on the proposed item at the index given

    if ( dragOK ) {
        return NSDragOperationMove;
    }
    else {
        return NSDragOperationNone;
    }
}


- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index
{           
    // Move the dragged item(s) to their new position in the data model
    // and reload the view or move the rows in the view.
    // This is of course quite dependent on your implementation        
}

There is a good deal of code needed to be filled in for the last method, and regarding the animated movement, Apple's complex table view demo code mentioned in samir's comment is quite useful, if somewhat complex to decipher.

Related Topic