I am trying (unsuccessfully) to build an NSOutlineView running a TreeController. I went through a bunch of tutorials, but they all preload the data before starting anything, and that won't work for me.
I have a simple class for a device:
import Cocoa class Device: NSObject { let name : String var children = [Service]() var serviceNo = 1 var count = 0 init(name: String){ self.name = name } func addService(serviceName: String){ let serv = "\(serviceName) # \(serviceNo)" children.append(Service(name: serv)) serviceNo += 1 count = children.count } func isLeaf() -> Bool { return children.count < 1 } }
I also have an even simpler class for "Service":
import Cocoa class Service: NSObject { let name: String init(name: String){ self.name = name } }
Finally, I have a ViewController:
class ViewController: NSViewController { var stepper = 0 dynamic var devices = [Device]() override func viewDidLoad() { super.viewDidLoad() } override var representedObject: Any? { didSet {
Obviously, I have two buttons, one of which adds a “device” and the other a “service” for each device.
What I cannot do is any of this data in NSOutlineView. I set the Control property of the TreeController object to: Class and Class: Device mode, and without setting the Children, Count or Leaf properties, I get (predictably):
2017-01-04 17: 20: 19.337129 OutlineTest [12550: 1536405] Attention: [object class: device] childrenKeyPath cannot be nil. To resolve this log message, set the childrenKeyPath attribute in Interface Builder
If I set the Children property to "children", everything will be very bad:
2017-01-04 17: 23: 11.150627 OutlineTest [12695: 1548039] [General] [addObserver: forKeyPath: options: context:] is not supported. The path to the key: children
All I'm trying to do is configure NSOutlineView to enter data from the NSTreeController so that when a new “device” is added to the device array [], it displays as a structure.
If anyone could point me in the right direction, I would be very grateful.
Many thanks to Warren for his extremely useful work. I have a (mostly) job. A few things I also needed to do besides Warren's suggestions:
Define the data store for the tree controller
Link OutlineView to TreeController
Associate a column with a TreeController
Bind a TableView element to a table cell view (yes, really)
As soon as all that was done, I had to play a little with the actual data store:
var name = "Bluetooth Devices Root" var deviceStore = [Device]() @IBOutlet var treeController: NSTreeController! @IBOutlet weak var outlineView: NSOutlineView! override func viewDidLoad() { super.viewDidLoad() deviceStore.append(Device(name: "Bluetooth Devices")) self.treeController.content = self } override var representedObject: Any? { didSet {
It turns out that the root cannot be without children at the beginning, at least as far as I can tell. When I add the child, I can delete the value of the owner of the place, and the tree seems to work (basically) the way I want. Another thing is that I have to reload the data and re-display the outline every time the data changes:
outlineView.reloadData() outlineView.setNeedsDisplay()
Nothing without it. I still don't have the correct data update (see comments below Warren's answer), but I'm almost there.