Reputation: 4258
i would like to realize an outlineView, which shows string values as root values. the following code works for me:
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
}
}
This is the result:
but i don't know, how i can assign different string values for Item 1 (for example). i wish to realize something like that:
+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
...
can somebody help me?
Upvotes: 2
Views: 1359
Reputation: 6151
A simple String
array will not be too much of use here, you need a dictionary at least to display sub items. I would recommend to introduce a little helper model, lets call it Item
. It holds a name, and a number of children Items, those will be the 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
}
}
Declare a property on your viewController called items
, and return some Item
's using the itemsForNumberOf
helper function.
class TestController: NSViewController, NSOutlineViewDataSource, NSOutlineViewDelegate {
let items: [Item] = {
return Item.itemsForNumberOf(parents: 5, childrens: 3)
}()
@IBOutlet weak var outlineView: NSOutlineView!
}
In TestController
override the viewDidLoad()
function and assign the delegate and datasource to your viewController.
override func viewDidLoad() {
super.viewDidLoad()
self.outlineView.delegate = self
self.outlineView.dataSource = self
}
Check the documentation of 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 expandalbe property based on children of the received Item.
If it is empty, no children -> not expandable
If it is not empty, has children -> expandable
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
guard let item = item as? Item else {
return false
}
return !item.childrens.isEmpty
}
Same for returning the sub items count by using childrens property again
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
}
In your viewFor
function, you need to make sure to return nil
to everything, what is not a Item
type asking for a view.
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
}
}
And you should end up with something like this:
Upvotes: 1
Reputation: 15589
If you want to display hierarchical data, the data can't be a simple array of strings. Instead of item strings, use dictionaries or custom objects which have title/name and children properties. The children property is an array of child items.
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any
returns the children of item
. If item
is "Item 1" and index
is 1, the return value is "Sub Item 1.2".
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int
returns the number of children of ``. If item
is "Item 1" , the return value is 2.
Upvotes: 0