1

In my app, I have an NSOutlineView that gets its data from a NSTreeController -- which in turn gets it from the Core Data model.

What I would like to do now is to add group headings and maybe some additional rows to the outline view -- obviously things that should exist outside of the model and be part of the view. But, as much as I scratch my head over this, I can't think of any way to make the outline view display these things without modifying the underlying model, which is obviously a big no-no.

Your help is very appreciated. I feel like I am missing something obvious here...

Marcel Hansemann
  • 1,019
  • 8
  • 11

1 Answers1

4

What you would do here is to write a custom NSTreeController subclass. Here is why this is the perfect place for the changes you want to do:

  • It's not in the model, as you said.
  • Has nothing to do with the view -- completely transparent.
  • Basically what you want is to create displayed data out of saved data <- this is a controller's task.

Luckily, the Controller classes in Cocoa are very powerful and very simple at the same this. For you it should be enough to override the -arrangedObjects method. Re-use the default implementation, as it does a lot of useful things like applying predicates or sorting. Here's how this could look like:

- (id)arrangedObjects {
  id root = [super arrangedObjects];

  // "root" is something like a NSTreeNode but not quite yet
  NSTreeNode *newRoot = [NSTreeNode treeNodeWithRepresentedObject: nil];
  [[newRoot mutableChildNodes] setArray: [root childNodes]];

  // Do your customizations here

  return newRoot;
}

The returned object is of the class NSTreeNode - see the documentation on how to do modifications.

Max Seelemann
  • 9,344
  • 4
  • 34
  • 40
  • Can't believe I haven't considered this approach before. One correction: `-arrangedObjects` does not return an object of the `NSTreeNode` class, but an object that handles `-childNodes` and `-descendantNodeAtIndexPath:` messages. Also, it might prove better to override `-rearrangeObjects` and do your work there. – Mike Abdullah Nov 15 '10 at 14:14
  • Thanks, it seems like a step in the right direction. But NSTreeController is woefully opaque and, as Mike wrote, only returns bizarro _NSTreeProxy objects that are not derived from NSTreeNode. And that means I can't use mutableChildNodes, so I'm still investigating this. – Marcel Hansemann Nov 16 '10 at 11:15
  • Alright. Then just use the `-childNodes` method to get the nodes, create a separate `NSTreeNode` and return that one! (I'll adjust the answer in a second) – Max Seelemann Nov 16 '10 at 20:23
  • @Mike: `-rearrangeObjects` does barely anything else than calling `-arrangedObjects` and cache the result. The one I used in my example is probably the one to override... – Max Seelemann Nov 16 '10 at 20:27
  • Sadly, it doesn't work that easily. Reading it here, it *should* work, but the issue is the opaque `_NSTreeProxy` root node. If you return an `NSTreeNode` instead, nothing will work anymore and you'll get messages that the tree node doesn't handle a `_valueForKeyPath:ofObjectAtIndexPath:` message. So I implemented my own proxy object, an `NSTreeNode` subclass, and tried to implement that method myself. But it doesn't work. When it gets called, I don't get access to anything. My root node proxy apparently has no child nodes, because `[[self childNodes] count]` returns 0. Sometimes I hate Cocoa. – Marcel Hansemann Nov 18 '10 at 20:53
  • 1
    Then you might have to bite the bitter apple and program your own tree controller. It's very sad, these APIs don't work as they are supposed to ... – Max Seelemann Nov 24 '10 at 02:09