8

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.

Samir
  • 627
  • 4
  • 9
Roger Gilbrat
  • 3,755
  • 5
  • 34
  • 58
  • You mean drag a view then drop into itself? – Mil0R3 Sep 04 '12 at 07:13
  • Check this [code example](https://developer.apple.com/library/mac/#samplecode/TableViewPlayground/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010727). It has some tips to some advanced NSTableView/NSOutlineView operations. – Samir Sep 04 '12 at 07:50
  • Just for reference heres another link explaining it really well --> http://www.cocoabuilder.com/archive/cocoa/133120-drag-and-drop-in-nsoutlineview.html – Just a coder Jul 13 '13 at 05:15

2 Answers2

14

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.

Monolo
  • 18,205
  • 17
  • 69
  • 103
  • Yeah, sorry for the poorly worded question, it's what happens when I post late at night. Your solution looks like what I need. I'll try it out as soon as I get home tonight. – Roger Gilbrat Sep 04 '12 at 16:04
  • Thanks, this worked perfectly except I had to make the pasteboard accept NSStrings rather then a user type, which was easier anyway. Thanks! – Roger Gilbrat Sep 05 '12 at 02:15
  • 1
    I got "No known instance method for selector 'uniqueStringRepresentation'". What was it? – Andrew Chang Nov 27 '13 at 08:19
  • Ya, what's uniqueStringRepresentation? Doesn't seem to be a nsoutliveview method. – imns May 20 '14 at 02:47
  • 1
    @bababa It is just intended as a placeholder for some method to generate a unique string representation for the item. You will have to write it for yourself, based on your items - only you know how they are defined and how you can make a unique representation of them. – Monolo May 20 '14 at 09:28
  • Hmm okay, does it have to be a string? – imns May 20 '14 at 15:44
  • 1
    @bababa Yes in the code shown here it does, because it is needed as a parameter for the `setString:forType:` method of `NSPasteboardItem`. Have a look at the [documentation](https://developer.apple.com/library/mac/documentation/cocoa/reference/NSPasteboardItem_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40008163-CH1-DontLinkElementID_1) for other options. – Monolo May 20 '14 at 16:05
9

I have created a small sample project which has an NSOutlineView where you can add and remove items.

Today I have implemented Drag and Drop support based on @Monolo's answer (See changes).

screenshot

Besi
  • 22,579
  • 24
  • 131
  • 223