加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

swift – 将NSTreeController与NSOutlineView一起使用

发布时间:2020-12-14 04:42:55 所属栏目:百科 来源:网络整理
导读:我正在尝试(不成功)构建一个TreeController控制的NSOutlineView.我已经阅读了很多教程,但他们都在开始之前预先加载了数据,这对我来说不起作用. 我有一个简单的设备类: import Cocoaclass Device: NSObject { let name : String var children = [Service]()
我正在尝试(不成功)构建一个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的对象控制器属性设置为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视图中.

如果有人能指出我正确的方向,我将非常感激.

非常感谢沃伦的非常有益的工作.我(大部分)都在工作.除了沃伦的建议之外,我还需要做一些事情:

Set the datastore for the TreeController


设置树控制器的数据存储

Bind to the TreeView


将OutlineView绑定到TreeController

Bind the Column to the TreeController


将列绑定到TreeController

Bind the Table View Cell to the Table Cell View


将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的回答下面的评论),但我差不多了.

解决方法

为了说明这一点,NSTreeController管理一个对象树,所有这些对象都需要回答以下三个问题/请求.

>你是一片叶子,即你没有孩子吗? = 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有更多信息.

编辑

除了上面的内容之外,您还需要将大纲视图绑定到树控制器.

首先确保您的大纲视图处于查看模式.

place outline view in view mode

接下来将表列绑定到树控制器上的arrangeObjects.

bind column to tree controller

最后将文本单元格绑定到相关的键路径.在你的情况下它的名字. objectValue是对单元格中对象的引用.

bind data to cell

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读