Swift 3 - 带有可扩展项的 NSOutlineView

Swift 3 - NSOutlineView with expandable items

我想实现一个 outlineView,它将字符串值显示为根值。以下代码对我有用:

import Cocoa


class TestController: NSViewController, NSOutlineViewDataSource, NSOutlineViewDelegate {


    @IBOutlet weak var outlineView: NSOutlineView!

var items: [String] = ["Item 1", "Item 2", "Item 3", "Item 4","Item 5"]

func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
        return items[index]
    }


    func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
        return true
    }


    func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
        if item == nil {
         return items.count
        }
        return 0
    }


    func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {

        let v = outlineView.make(withIdentifier: "HeaderCell", owner: self) as! NSTableCellView
        if let tf = v.textField {
            tf.stringValue = item as! String
        }
        return v
    }

}

这是结果:

但我不知道如何为项目 1 分配不同的字符串值(例如)。我希望实现这样的事情:

+Item 1
++Sub Item 1.1
++Sub Item 1.2

+Item 2
++Sub Item 2.1

+Item 3
++Sub Item 3.1
++Sub Item 3.2
++Sub Item 3.3

...

有人可以帮我吗?

如果要显示分层数据,数据不能是简单的字符串数组。使用具有 title/name 和 children 属性的字典或自定义 objects 而不是项目字符串。 children 属性 是 child 项的数组。

func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Anyreturn是item的child人。如果 item 为 "Item 1" 且 index 为 1,则 return 值为 "Sub Item 1.2"。

func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int returns``的children数。如果 item 为 "Item 1" ,则 return 值为 2.

一个简单的String数组在这里用处不大,你至少需要一个字典来显示子项。我建议引入一个小辅助模型,我们称之为 Item。它包含一个名称和一些 children 项,这些将是 sub-items.

struct Item {
    let name: String
    var childrens: [Item] = []

/// Create a helper function to recursivly create items
///
/// - Parameters:
///   - parents: number of parent items 
///   - childrens: number of children for each parent item
/// - Returns: array of Item
static func itemsForNumberOf(parents: Int, childrens: Int) -> [Item] {
    var items: [Item] = []
    for parentID in 1...parents {
        var parent = Item(name: "Index \(parentID)",childrens: [])
        for childrenID in 1...childrens {
            let children = Item(name: "Index \(parentID).\(childrenID)",childrens: [])
            parent.childrens.append(children)
        }
        items.append(parent)
    }
    return items
}

}

在名为 items 的 viewController 上声明一个 属性,并使用 itemsForNumberOf 辅助函数声明 return 一些 Item

class TestController: NSViewController, NSOutlineViewDataSource, NSOutlineViewDelegate {

    let items: [Item] = {
        return Item.itemsForNumberOf(parents: 5, childrens: 3)
    }()

    @IBOutlet weak var outlineView: NSOutlineView!
}

TestController 中覆盖 viewDidLoad() 函数并将委托和数据源分配给您的 viewController。

override func viewDidLoad() {
    super.viewDidLoad()
    self.outlineView.delegate = self
    self.outlineView.dataSource = self
}

查看 NSOutlineViewDataSource and in specific this API

的文档
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
    if item == nil {
        return items[index]
    }

    if let item = item as? Item, item.childrens.count > index {
            return item.childrens[index] 
    }

    return ""
}

Return 根据收到的项目 children 展开 属性。
如果为空,则没有children -> 不可扩展
如果它不为空,则有 children -> expandable

func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
    guard let item = item as? Item else {
        return false
    }

    return !item.childrens.isEmpty
}

同样 return 再次使用 childrens 属性 对子项进行计数

func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
    if item == nil {
        return items.count
    }

    if let item = item as? Item {
        return item.childrens.count
    }

    return 0
}

在您的 viewFor 函数中,您需要确保 return nil 所有内容,而不是 Item 类型的请求视图。

    func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {

        guard let item = item as? Item else {
            return nil
        }

        let v = outlineView.make(withIdentifier: "HeaderCell", owner: self) as! NSTableCellView
        if let tf = v.textField {
            tf.stringValue = item.name
        }
        return v
    }
}

你应该得到这样的结果: