Как использовать NSOutlineView без привязок - PullRequest
0 голосов
/ 16 июня 2020

Мои данные выглядят так:

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"
    ]
]

enter image description here

Я хочу, чтобы NSOutlineView выглядел как на картинке выше.

1 Ответ

0 голосов
/ 16 июня 2020

Входные данные

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"
    ]
]

Словарь - это неупорядоченная коллекция, и это вызовет проблемы, когда вы захотите получить доступ к элементам через индексы, ... Давайте создадим настраиваемую структуру, которая будет предоставлять данные для 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]!)
        }
    }
}

Эта структура является примером того, как вы можете это сделать, но есть много других способов. Это действительно зависит от того, что вы планируете здесь делать. Вы можете переключиться на объект (вместо структуры), ...

Ключевым моментом здесь является то, что свойство children является упорядоченной коллекцией (массивом).

Контроллер представления

Добавить свойство 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

Как следует из названия, источник данных предоставляет только данные. У нас уже есть свойство 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

Среди прочего, делегат предоставляет представление ячейки для отображения для определенного элемента и столбца.

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

Пример проекта

  • Создание нового приложения в Xcode (macOS - Swift & Storyboard)
  • Добавить представление структуры
    • Установить ограничения
    • Подключите делегат и источник данных к контроллеру представления.
  • Щелкните вид схемы и установите
    • Столбцы: 3
    • Снять отметку с заголовков, переупорядочить
  • Установить идентификатор 1-го, 2-го и 3-го столбца на TitleColumn, LocColumn, JsonColumn
  • Установить для идентификатора представления ячейки таблицы 1-го, 2-го и 3-го столбца значение TitleCell, LocCell, JsonCell

enter image description here

...