【发布时间】:2017-01-04 22:25:32
【问题描述】:
我正在尝试(不成功)构建一个由 TreeController 控制的 NSOutlineView。我已经阅读了一堆教程,但是它们都在开始任何事情之前预先加载了数据,这对我不起作用。
我有一个简单的设备类:
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 的 Object Controller 属性设置为 Mode: Class 和 Class: Device,并且没有设置 Children、Count 或 Leaf 属性,我得到(可以预见):
2017-01-04 17:20:19.337129 OutlineTest[12550:1536405] 警告:[对象类:设备] childrenKeyPath 不能为 nil。要消除此日志消息,请在 Interface Builder 中设置 childrenKeyPath 属性
如果我随后将 Children 属性设置为 'children',事情会变得非常糟糕:
2017-01-04 17:23:11.150627 OutlineTest[12695:1548039] [常规] [ addObserver:forKeyPath:options:context:] 不受支持。关键路径:儿童
我要做的只是将 NSOutlineView 设置为从 NSTreeController 获取输入,以便在将新的“设备”添加到 devices[] 数组时,它会显示在大纲视图中。
如果有人能在这里指出正确的方向,我将不胜感激。
非常感谢 Warren 所做的非常有帮助的工作。我已经(大部分)工作了。除了 Warren 的建议,我还需要做几件事:
将 OutlineView 绑定到 TreeController
将 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 答案下方的 cmets),但我快到了。
【问题讨论】:
-
您可以尝试完全设置数组,而不是插入,这样 KVO 通知应该正确触发。
-
我实际上做不到。实际上,我会扫描网络设备并在找到它们时添加它们,一旦这有效,我无法预加载它们,并且必须在找到它们时插入它们。
-
你可以用一个不可变的数组来做到这一点,而不是写
devices.append(Device(...)),你写devices = devices + [Device(...)],因此devices的值在每次找到新设备时都会改变。 -
或者,您可以使用树控制器进行插入。
标签: swift macos nsoutlineview nstreecontroller