簡體   English   中英

將NSTreeController與NSOutlineView一起使用

[英]Using NSTreeController with NSOutlineView

我正在嘗試(不成功)構建一個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]警告:[object class:Device] childrenKeyPath不能為nil。 要消除此日志消息,請在Interface Builder中設置childrenKeyPath屬性

如果我然后將Children屬性設置為'children'則會非常糟糕:

2017-01-04 17:23:11.150627 OutlineTest [12695:1548039] [General] [addObserver:forKeyPath:options:context:]不受支持。 關鍵路徑:兒童

我要做的就是設置NSOutlineView來從NSTreeController獲取輸入,這樣當一個新的'Device'被添加到devices []數組時,它會顯示在Outline視圖中。

如果有人能指出我正確的方向,我將非常感激。

非常感謝沃倫的非常有益的工作。 我(大部分)都在工作。 除了沃倫的建議之外,我還需要做一些事情:

設置TreeController的數據存儲區 設置樹控制器的數據存儲

綁定到TreeView 將OutlineView綁定到TreeController

將列綁定到TreeController 將列綁定到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的回答下面的評論),但我差不多了。

為了說明這一點, NSTreeController管理一個對象樹,所有這些對象都需要回答以下三個問題/請求。

  • 你是一片葉子,即你沒有孩子嗎? = leafKeyPath
  • 如果你不是葉子,你有多少個孩子? = countKeyPath
  • 把你的孩子給我! = childrenKeyPath

它很容易在IB或編程中設置它們。 分別是一組相當標准的屬性。

  • isLeaf
  • childCount
  • children

但它完全是任意的,可以是回答這些問題的任何屬性集。

我通常會設置一個名為TreeNode的協議,並使我的所有對象都符合它。

@objc protocol TreeNode:class { 
    var isLeaf:Bool { get }
    var childCount:Int { get }
    var children:[TreeNode] { get } 
}

對於您的Device對象,您可以使用isLeafchildren回答2個問題,但不回答childCount問題。

您的設備的子項是Service對象,它們不會回答這一點,這也是您獲得例外的原因之一。

因此,要修復代碼,可能的解決方案是......

服務對象

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
    } 

現在,每次附加設備或添加服務時,只需在reloadItem上調用outlineView (您還需要一個插座)

@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)    
    }

這是基礎知識,應該讓你開始,但NSOutlineViewNSTreeController的文檔有更多的信息。

編輯

除了上面的內容之外,您還需要將大綱視圖綁定到樹控制器。

首先確保您的大綱視圖處於查看模式。

在視圖模式下放置大綱視圖

接下來的表格列綁定到樹控制器上arrangedObjects。

將列綁定到樹控制器

最后將文本單元格綁定到相關的鍵路徑。 在你的情況下它的名字 objectValue是對單元格中對象的引用。

將數據綁定到單元格

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM