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 } }
我还有一个更简单的“服务”课程:
import Cocoa class Service: NSObject { let name: String init(name: String){ self.name = name } }
最后,我有ViewController:
class ViewController: NSViewController { var stepper = 0 dynamic var devices = [Device]() override func viewDidLoad() { super.viewDidLoad() } override var representedobject: Any? { didSet { // Update the view,if already loaded. } } @IBAction func addDeviceAction(_ sender: Any) { let str = "New Device #\(stepper)" devices.append(Device(name: str)) stepper += 1 print("Added Device: \(devices[devices.count-1].name)") } @IBAction func addService(_ sender: Any) { for i in 0..<devices.count { devices[i].addService(serviceName: "New Service") } } }
显然我有2个按钮,一个添加“设备”,另一个为每个设备添加“服务”.
我无法实现的是NSOutlineView中显示的任何数据.我已经将TreeController的对象控制器属性设置为Mode:Class和Class:Device,并且没有设置我得到的Children,Count或Leaf属性(可预测):
2017-01-04 17:20:19.337129 OutlineTest[12550:1536405] Warning: [object class: Device] childrenKeyPath cannot be nil. To eliminate this log message,set the childrenKeyPath attribute in Interface Builder
如果我然后将Children属性设置为’children’则会非常糟糕:
2017-01-04 17:23:11.150627 OutlineTest[12695:1548039] [General] [ addobserver:forKeyPath:options:context:] is not supported. Key path: children
我要做的就是设置NSOutlineView来从nstreecontroller获取输入,这样当一个新的’Device’被添加到devices []数组时,它会显示在Outline视图中.
如果有人能指出我正确的方向,我将非常感激.
非常感谢沃伦的非常有益的工作.我(大部分)都在工作.除了沃伦的建议之外,我还需要做一些事情:
将TableView单元格绑定到表格单元格视图(是的,真的)
完成所有这些后,我不得不稍微使用实际的数据存储区:
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 { // Update the view,if already loaded. } } @IBAction func addDeviceAction(_ sender: Any) { if(deviceStore[0].name == "Bluetooth Devices"){ deviceStore.remove(at: 0) }
事实证明,Root在开始时不能没有孩子,至少就我所知.一旦我添加了一个孩子,我就可以删除占位符值,并且树似乎可以正常工作(大部分).另一件事是我必须重新加载数据并在数据发生变化时重新显示大纲:
outlineView.reloadData() outlineView.setNeedsdisplay()
没有它,没有.我仍然没有正确更新数据(请参阅Warren的回答下面的评论),但我差不多了.
解决方法
>你是一片叶子,即你没有孩子吗? = leafKeyPath
>如果你不是叶子,你有多少个孩子? = countKeyPath
>把你的孩子给我! = childrenKeyPath
它很容易在IB或编程中设置它们.分别是一组相当标准的属性.
> isLeaf
> childCount
>孩子们
但它完全是任意的,可以是回答这些问题的任何属性集.
我通常会设置一个名为TreeNode的协议,并使我的所有对象都符合它.
@objc protocol TreeNode:class { var isLeaf:Bool { get } var childCount:Int { get } var children:[TreeNode] { get } }
对于您的Device对象,您可以使用isLeaf和children回答2个问题,但不回答childCount问题.
您的设备的子项是服务对象,它们不会回答这一点,这也是您获得例外的原因之一.
服务对象
class Service: NSObject,TreeNode { let name: String init(name: String){ self.name = name } var isLeaf:Bool { return true } var childCount:Int { return 0 } var children:[TreeNode] { return [] } }
Device对象
class Device: NSObject,TreeNode { let name : String var serviceStore = [Service]() init(name: String){ self.name = name } var isLeaf:Bool { return serviceStore.isEmpty } var childCount:Int { return serviceStore.count } var children:[TreeNode] { return serviceStore } }
从MVC的角度来看,这是一个可怕的事情,但这个答案很方便.根对象.
class ViewController: NSViewController,TreeNode { var deviceStore = [Device]() var name = "Henry" //whatever you want to name your root var isLeaf:Bool { return deviceStore.isEmpty } var childCount:Int { return deviceStore.count } var children:[TreeNode] { return deviceStore } }
所以你需要做的就是设置treeController的内容.让我们假设您在ViewController中有一个IBOutlet.
class ViewController: NSViewController,TreeNode { @IBOutlet var treeController:nstreecontroller! @IBOutlet var outlineView:NSOutlineView! override func viewDidLoad() { super.viewDidLoad() treeController.content = self }
现在,每次附加设备或添加服务时,只需在outlineView上调用reloadItem(您还需要一个插座)
@IBAction func addDeviceAction(_ sender: Any) { let str = "New Device #\(stepper)" devices.append(Device(name: str)) stepper += 1 print("Added Device: \(devices[devices.count-1].name)") outlineView.reloadItem(self,reloadChildren: true) }
这是基础,应该让你开始,但NSOutlineView&的文档. nstreecontroller有更多信息.
编辑
除了上面的内容之外,您还需要将大纲视图绑定到树控制器.
首先确保您的大纲视图处于查看模式.
接下来将表列绑定到树控制器上的arrangeObjects.
最后将文本单元格绑定到相关的键路径.在你的情况下它的名字. objectValue是对单元格中对象的引用.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。