简体   繁体   English

如何在没有绑定的情况下使用 NSOutlineView

[英]How to use NSOutlineView without bindings

My data looks like:我的数据看起来像:

let keysDetails : [String : Any] = [
    "kAddToBag" : [
        "locDict" : [
            "at" : "add",
            "ae" : "Add"
        ],
        "jsonDict" : [
            "at" : "add to bag",
            "ae" : "ADD TO BAG"
        ],
        "path" : "somepath"
    ],
    "kShopTab" : [
        "locDict" : [
            "be_fr" : "shop",
            "be_nl" : "SHOP"
        ],
        "jsonDict" : [
            "be_fr" : "shop",
            "be_nl" : "SHOP"
        ],
        "path" : "somepath2"
    ]
]

在此处输入图像描述

I want NSOutlineView to look like above picture.我希望 NSOutlineView 看起来像上图。

Input data输入数据

let keysDetails : [String : Any] = [
    "kAddToBag" : [
        "locDict" : [
            "at" : "add",
            "ae" : "Add"
        ],
        "jsonDict" : [
            "at" : "add to bag",
            "ae" : "ADD TO BAG"
        ],
        "path" : "somepath"
    ],
    "kShopTab" : [
        "locDict" : [
            "be_fr" : "shop",
            "be_nl" : "SHOP"
        ],
        "jsonDict" : [
            "be_fr" : "shop",
            "be_nl" : "SHOP"
        ],
        "path" : "somepath2"
    ]
]

Dictionary is an unordered collection and it will cause issues when you'll want to access elements via indexes, ... Let's create a custom structure which will provide data for the NSOutlineView .字典是一个无序的集合,当你想通过索引访问元素时它会导致问题,......让我们创建一个自定义结构,它将为NSOutlineView提供数据。

struct Item {
    let title: String      // First column value
    let loc: String        // Second column value
    let json: String       // Third column value
    let children: [Item]   // Possible children

    init(title: String, loc: String, json: String, children: [Item] = []) {
        self.title = title
        self.loc = loc
        self.json = json
        self.children = children
    }

    init?(title: String, content: Any) {
        // Check that the content is a dictionary and that it contains
        // locDict & jsonDict and both are dictionaries
        guard let content = content as? [String: Any],
            let loc = content["locDict"] as? [String: String],
            let json = content["jsonDict"] as? [String: String] else {
                return nil
        }

        // Check that both dictionaries contains same keys
        let locKeys = loc.keys.sorted()
        let jsonKeys = json.keys.sorted()
        guard locKeys == jsonKeys else {
            return nil
        }

        // Initialize top level item
        self.title = title
        self.loc = "locDict"
        self.json = "jsonDict"
        self.children = locKeys.map { key in
            // We can force unwrap here because we already checked that
            // both dictionaries contains same keys
            Item(title: key, loc: loc[key]!, json: json[key]!)
        }
    }
}

This structure is an example how you can do it, but there're lot of other ways.这个结构是一个例子,你可以如何做到这一点,但还有很多其他的方法。 It really depends on what you're planning to do here.这真的取决于你打算在这里做什么。 You can switch to an object (instead of a structure), ...您可以切换到 object(而不是结构),...

The key point here is that the children property is an ordered collection (array).这里的关键点是children属性是一个有序集合(数组)。

View Controller查看 Controller

Add items property (again, an ordered collection = array).添加items属性(再次,有序集合 = 数组)。

class ViewController: NSViewController {
    private let items: [Item] = {
        // Map keysDetails to an array of our Item structures
        keysDetails.compactMap { (key: String, value: Any) in
            Item(title: key, content: value)
        }
    }()
}

NSOutlineViewDataSource NSOutlineViewDataSource

As the name implies, data source provides just data.顾名思义,数据源只提供数据。 We already have the items property, let's use it.我们已经有了items属性,让我们使用它。

extension ViewController: NSOutlineViewDataSource {
    func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
        if item == nil {
            // item == nil
            // We're being asked for the number of top level elements (kAddToBag, ...)
            return items.count
        }

        // Develop time (debug) - check that the item is really Item
        assert(item is Item);

        // item != nil
        // We're being asked for the number of children of an item
        return (item as! Item).children.count
    }

    func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
        if item == nil {
            // item == nil
            // We're being asked for n-th (index) top level element
            return items[index]
        }

        // Develop time (debug) - check that the item is really Item
        assert(item is Item);

        // item != nil
        // We're being asked for n-th (index) child of an item
        return (item as! Item).children[index]
    }

    func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
        // Develop time (debug) - check that the item is really Item
        assert(item is Item);

        // Item is expandable only if it has children
        return (item as! Item).children.count > 0
    }
}

NSOutlineViewDelegate NSOutlineViewDelegate

Among other stuff, delegate provides cell view to display for particular item and column.除其他外,委托提供单元格视图以显示特定项目和列。

extension ViewController: NSOutlineViewDelegate {
    func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
        // Get the column identifier and our Item
        guard let columnIdentifier = tableColumn?.identifier,
            let item = item as? Item else {
                return nil
        }

        // Get a cell view identifier and an actual value we should display
        let cellViewIdentifier: String
        let stringValue: String

        switch columnIdentifier.rawValue {
        case "TitleColumn":
            cellViewIdentifier = "TitleCell"
            stringValue = item.title
        case "LocColumn":
            cellViewIdentifier = "LocCell"
            stringValue = item.loc
        case "JsonColumn":
            cellViewIdentifier = "JsonCell"
            stringValue = item.json
        default:
            return nil
        }

        // Make a view from the cell view identifier
        let view = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(cellViewIdentifier), owner: self) as? NSTableCellView
        // Update text field value
        view?.textField?.stringValue = stringValue
        return view
    }
}

Sample project示例项目

  • Create a new app in Xcode (macOS - Swift & Storyboard)在 Xcode 中创建一个新应用程序(macOS - Swift & Storyboard)
  • Add the outline view添加大纲视图
    • Set constraints设置约束
    • Connect delegate & datasource to the view controller将委托和数据源连接到视图 controller
  • Click on the outline view and set单击大纲视图并设置
    • Columns: 3列数:3
    • Uncheck Headers, Reordering取消选中标题,重新排序
  • Set 1st, 2nd and 3rd column identifier to TitleColumn , LocColumn , JsonColumn将第 1、第 2 和第 3 列标识符设置为TitleColumnLocColumnJsonColumn
  • Set 1st, 2nd and 3rd column table cell view identifier to TitleCell , LocCell , JsonCell将第 1、第 2 和第 3 列表格单元格视图标识符设置为TitleCellLocCellJsonCell

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM