Входные данные
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
