0

I've built an NSOutlineView that gets dynamically updated data from an NSTreeController and that all works fine. What I can't seem to do is work backwards from there based on a user selection in the NSOutlineView.

    var deviceStore = [TreeNode]()

is my backing datastore that is updated in real-time it is an array of Device Objects, which may )or may not) contain an array of Service objects as children.

This all works. But when I select a row in the Outline View, I need to work my way back to the original object in the deviceStore -- or, at the very least, get the displayed data from the OutlineView so that I can walk the deviceStore to find the original item.

What I've got is func outlineViewSelectionDidChange(_ notification: Notification) {} which is called when a selection is made, and I can, from that, extract the NSTreeController TreeNode via treeController.selectedNodes but from there, I am in the weeds. The selectedNodes is the complete array of the selected Node, so if it's a child (leaf) node, it includes its parent node, and all its siblings.

Give then Table shown here: OutlineView

The selectedNodes array looks like this:

<NSTreeControllerTreeNode: 0x6080000c4590>, child nodes {
    0:<NSTreeControllerTreeNode: 0x6000000ca6b0>, child nodes {
        0:<NSTreeControllerTreeNode: 0x6000000caf70>, child nodes {}
        1:<NSTreeControllerTreeNode: 0x6000000cafe0>, child nodes {}
        2:<NSTreeControllerTreeNode: 0x6000000cb050>, child nodes {}
    }
    1:<NSTreeControllerTreeNode: 0x6080000d1790>, child nodes {
        0:<NSTreeControllerTreeNode: 0x6000000cce80>, child nodes {}
    }
}

And the selectedIndex is 4.

I can't see how to get back to what, in my data model, would be deviceStore[0].serviceStore[2] from this information.

If I could retrieve the value in the Service ID column from the selected Row, I could simply walk the deviceStore tree to find it.

I'm sure there's a simply, elegant, easy way to do this that I just haven't found yet, but being new to NSTreeControllers and NSOutlineViews I'm lost.

Davidgs
  • 411
  • 6
  • 18

3 Answers3

1

You may try to access directly the associated object(s) like this:

let selectedService = treeController.selectedObjects.first as? Service

The docs are here.

also make sure your NSTreeController is correctly configured to use your class' objects:

enter image description here

Alternatively (if you want to work directly with your data source) you may want to get the index path of the selected object in the NSTreeController:

var selectionIndexPath: IndexPath? { get }
Bogdan Farca
  • 3,856
  • 26
  • 38
  • Thanks for the reply. `let selectedService = treeController.selectedObjects.first as? Service` fails spectacularly since the treeController.selectedObject is not a `Service` object, nor is it a `Device` object. Actually, both of those Objects use a custom `TreeNode` protocol anyway, but it's not even a `TreeNode` Object. It is actually the entire `ViewController` Object (which also implements the `TreeNode` protocol. The selectionIndexPath **always** returns an index path value of 0. I'm stumped. – Davidgs Jan 07 '17 at 16:05
  • Edited the response with a possible explanation of why your tree controller isn't "serving" the correct objects. – Bogdan Farca Jan 07 '17 at 20:26
1

I use following with Core data,

func outlineViewSelectionDidChange(_ notification: Notification) {
    let item = outlineView.item(atRow: outlineView.selectedRow) as! NSTreeNode
    let fileItem = item.representedObject as! FileItemMO

    // Do what you need to do with object fileItem

}
Peter Ahlberg
  • 1,359
  • 2
  • 24
  • 26
0

I was never able to actually get back to the TreeController backing-store data baed on where the user clicks in the TreeView. What I was able to do was to work backwards to the data though.

func outlineViewSelectionDidChange(_ notification: Notification) {
    let selectedIndex = (notification.object as AnyObject).selectedRow!
    let selCol1 = outlineView.view(atColumn: 0, row: selectedIndex, makeIfNecessary: false)?.subviews.last as! NSTextField
    let selCol2 = outlineView.view(atColumn: 1, row: selectedIndex, makeIfNecessary: false)?.subviews.last as! NSTextField

    let devName = selCol1.stringValue
    let devID = selCol2.stringValue
...
}

I could then 'walk' the deviceStrore array until I found the devName and devID in it, and deal with it accordingly.

Probably not the most elegant solution, but at least it finally works.

Davidgs
  • 411
  • 6
  • 18