NSTreeController and Drag and Drop
I've posted enough about this to cocoa-dev that I think it's time to actually write it down. In Tiger, using NSTreeController with NSOutlineView means drag-and-drop doesn't work as expected. This definitely does not mean you should abandon bindings, especially if you're using Core Data. There are two key things you need to know.One: Unlike NSTableView, NSOutlineView expects you to implement all of the "required" datasource methods if you plan to use any of them. The simple solution is to provide dummy implementations:
#pragma mark -
#pragma mark NSOutlineView Hacks for Drag and Drop
- (BOOL) outlineView: (NSOutlineView *)ov
isItemExpandable: (id)item { return NO; }
- (int) outlineView: (NSOutlineView *)ov
numberOfChildrenOfItem:(id)item { return 0; }
- (id) outlineView: (NSOutlineView *)ov
child:(int)index
ofItem:(id)item { return nil; }
- (id) outlineView: (NSOutlineView *)ov
objectValueForTableColumn:(NSTableColumn*)col
byItem:(id)item { return nil; }
Two: When using NSTreeController, the "item" objects handed to you in the drag-and-drop datasource methods are actually instances of the private _NSArrayControllerTreeNode class. There's no real way to communicate with them directly. This may seem like a dead end, but through the magic of runtime binding, we can go ahead and use some private API to get at the "real" object:
- (BOOL) outlineView: (NSOutlineView *)ov
acceptDrop: (id )info
item: (id)item
childIndex: (int)index
{
item = [item observedObject];
// do whatever you would normally do with the item
}
This will generate a compiler warning but you can make it go away by making up some random NSObject category that declares -observedObject. As an extra bonus, when you're passed an array of items, you can take advantage of NSArray's -valueForKey: implementation:
NSArray *myItems = [items valueForKey:@"observedObject"];
That's all! I haven't tried this with the browser view, but I expect it's basically the same there.
Don't let this turn you off to NSTreeController in general. The things it does it does really well. In particular, recursive child relationships "just work," which is really useful when you have an entity with a relationship that references itself.
Some folks run for the hills at the first sign of trouble in bindings. In most cases, you'll end up with far less code by implementing a few workarounds. Make things easy on yourself and only write code where you have to.
NSTreeController and Drag and Drop
Posted Jun 18, 2005 — 17 comments below
Posted Jun 18, 2005 — 17 comments below
KB — Jun 19, 05 250
Scott Stevenson — Jun 19, 05 252
KB — Jun 19, 05 253
nobody — Jul 19, 05 308
Matt Holiday — Aug 21, 05 344
See http://homepage.mac.com/matthol2/cocoa/.
Jason Terhorst — Mar 06, 06 910
Matthieu Cormier — Mar 06, 06 911
http://allusions.sourceforge.net/articles/treeDragPart1.php
and
http://allusions.sourceforge.net/articles/treeDragPart2.php
Jason Terhorst — Mar 06, 06 913
Tim — Mar 09, 06 924
Is it safe to use the valueForKey:@"observedObject" for getting information about the private _NSArrayControllerTreeNode? I mean, what are the changes that Apple will continue to support this in-official attribute for future development?
Tim
Scott Stevenson — Mar 14, 06 931
In reality, even if this API did go away, the necessary changes would probably be very minor. A small price to pay for something that works in the here and now.
Frederick C. Lee — Mar 19, 06 941
I would love to see an example using CORE DATA + NSTreeController; specifically something with multiple nodes:
Region -->> Country -->> Province -->> City
| | | |
Maps Maps Maps Maps
Should I chain NSTreeControllers together?
How do I set up the respective Children Key Path/NSTreeController?
I understand the theory, but am getting confused with the implementation.
It appears I would need two (2) NSTreeControllers per region entity:
1) link to subRegion, 2) link to map.
That is, a Region's subRegion & map are siblings; one being a Leaf node (the map).
I would like to see a demo of something of this caliber.
Sam Stigler — May 07, 07 4064
Sam Stigler — May 08, 07 4079
[value valueForKey:@"observedObject"]
-(NSManagedObject *)observedObject { NSManagedObjectID *objID = [self objectID]; NSManagedObjectContext *moc = [self managedObjectContext]; id tmpObject = [moc objectWithID:objID]; return tmpObject; }
Rob — Oct 19, 07 4779
Paul Collins — Nov 23, 07 5134
I'm able to get my objects in -outlineView:writeItems:toPasteboard: using -representedObject the same as described for -observedObject in Scott's original post.
BTW, my results seem to differ with Sam's, in that -observedObject is still usable as described by Scott as of 10.4.11.
I'm not sure if the dummy NSOutlineViewDataSource methods are still needed in 10.5 (I'm leaving them in as I'm building for minimum 10.4 anyway).
Eric Man — Feb 01, 08 5433
Since I don't have 10.5 yet I couldn't take a look, but I think it is relevant to your post.
Josh — May 30, 09 6784
Where do I put this code?