Last week, I finally got around to building the number-one VirtualHostX feature request groups/folders in the sidebar. I had put off implementing this feature for years because I never knew quite where to start when it came to Core Data and NSOutlineView. But, with VHX 5.0 coming out later this year, I decided to lock myself in my office and not come out until I had a sorted, drag-and-droppable, outline view working. In the end, it only took about three hours to get all the pieces in place. And while Im not quite ready to release this feature to my users, I thought Id jot down some tips and code snippets that I found helpful while its still fresh in my mind. Google wasnt particularly helpful with any recent results on the topic, so maybe thisll be found by someone struggling with the same problem.
Setting up your XIB
In your Core Data model, create a one-to-many relationship called children pointing back to your same entity. Then create a corresponding one-to-one relationship called parent back to the same entity. The trick here is that youre not going to have an entity for groups/folders and then another entity for your actual items (in my case, virtual hosts). Theyre all going to be the same entity, but with a property called isFolder that allows you to differentiate between the two types.
In your NIB file youll need an array controller bound to your NSManagedObjectContext, set to Entity mode, and with a Fetch Predicate of parent == nil. This will fetch our top level objects entities without a parent.
Next, create a tree controller with its Children Key Path set to children, set to Entity mode, and its Content Array bound to your array controllers arrangedObjects.
Finally, drag an NSOutlineView to your NIB. Set its dataSource and delegate to one of those blue NSObjects, which youll subclass to be an OutlineViewController youll create in code later on.
Connect the IBOutlets back to the appropriate objects in your NIB.
Heres the first big chunk of OutlineViewController.h/m
Managing the outline items expanded state
Were so spoiled with how much crap work Cocoa handles for us, I naively thought that NSOutlineView would just automatically remember the collapsed/expanded state of the items in my tree. It turns out, there is a way to have NSOutlineView persist those states to NSUserDefaults, but I could never get that to work. So I just do it all manually its not that hard. Just add a BOOL to your Core Data model called isExpanded and then further down in
And then subclass your NSOutlineView's reloadData method to be:
And thats basically it. Overall, not as much code as I would have expected (thanks to bindings, natually). Hope the above code helps anyone tackling the same problem. Much of my code was discovered/cribbed from this great blog post on the topic from a few years ago.