如何编写 NSOutlineView?
How to program a NSOutlineView?
我在 Xcode 8 (Swift 3) 中创建 NSOutlineView 时遇到问题。我有一个 plist 文件,其中包含一些我想在 OutlineView 中显示的信息。 plist 文件如下所示(示例):
Root Dictionary *(1 item)
Harry Watson Dictionary *(5 items)*
name String Harry Watson
age Int 99
birthplace String Westminster
birthdate Date 01/01/1000
hobbies Array *(2 items)*
item 0 String Tennis
item 1 String Piano
OutlineView 应该看起来很相似,如下所示:
name Harry Watson
age 99
birthplace Westminster
birthdate 01/01/1000
> hobbies ... (<- this should be expandable)
我已经在 Google 上搜索了 NSOutlineView 教程,但我找到的都是 raywenderlich.com,所以我读了一点,但我认为这并不容易。
所以我想知道您是否可以帮助我完成上面的确切示例并给我一些代码示例,尤其是关于此功能的示例:
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {}
我不知道该写什么。
如果您有任何问题,请告诉我。
在此先致谢并致以诚挚的问候
我发现 Ray Wenderlitch 的教程质量参差不齐。那些假设你对 Swift 一无所知的内部笑话、冗长、一步一步的掌握对我来说太恶心了。这是一个简明教程,涵盖了手动和通过 Cocoa 绑定填充大纲视图的基础知识。
理解的关键 NSOutlineView
是您必须为每一行赋予一个唯一的标识符,可以是字符串、数字或表示该行的对象。 NSOutlineView
称之为 item
。基于此item
,您将查询数据模型以用数据填充大纲视图。
此答案提供了 3 种方法:
- 手动:以最基本的方式自己做所有事情。这是学习如何与
NSOutlineView
交互的一个很好的介绍,但我不建议将其用于生产代码。
- 精简:大纲视图仍然是手动填充,但方法更优雅。这是我自己的生产代码所用的。
- Cocoa 绑定:Mac OS X 的黄金时代留下的一些神奇的东西。虽然很方便,但不是未来的方式。将此视为高级主题
1。手动填充大纲视图
界面生成器设置
我们将使用一个非常简单的 NSOutlineView
,只有两列:Key 和 Value。
Select 第一列并将其标识符更改为 keyColumn
。然后 select 第二列并将其标识符更改为 valueColumn
:
将单元格的标识符设置为 outlineViewCell
。你只需要做一次。
代码
将以下内容复制并粘贴到您的 ViewController.swift
:
// Data model
struct Person {
var name: String
var age: Int
var birthPlace: String
var birthDate: Date
var hobbies: [String]
}
class ViewController: NSViewController {
@IBOutlet weak var outlineView: NSOutlineView!
// I assume you know how load it from a plist so I will skip
// that code and use a constant for simplicity
let person = Person(name: "Harry Watson", age: 99, birthPlace: "Westminster",
birthDate: DateComponents(calendar: .current, year: 1985, month: 1, day: 1).date!,
hobbies: ["Tennis", "Piano"])
let keys = ["name", "age", "birthPlace", "birthDate", "hobbies"]
override func viewDidLoad() {
super.viewDidLoad()
outlineView.dataSource = self
outlineView.delegate = self
}
}
extension ViewController: NSOutlineViewDataSource, NSOutlineViewDelegate {
// You must give each row a unique identifier, referred to as `item` by the outline view
// * For top-level rows, we use the values in the `keys` array
// * For the hobbies sub-rows, we label them as ("hobbies", 0), ("hobbies", 1), ...
// The integer is the index in the hobbies array
//
// item == nil means it's the "root" row of the outline view, which is not visible
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
if item == nil {
return keys[index]
} else if let item = item as? String, item == "hobbies" {
return ("hobbies", index)
} else {
return 0
}
}
// Tell how many children each row has:
// * The root row has 5 children: name, age, birthPlace, birthDate, hobbies
// * The hobbies row has how ever many hobbies there are
// * The other rows have no children
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
if item == nil {
return keys.count
} else if let item = item as? String, item == "hobbies" {
return person.hobbies.count
} else {
return 0
}
}
// Tell whether the row is expandable. The only expandable row is the Hobbies row
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
if let item = item as? String, item == "hobbies" {
return true
} else {
return false
}
}
// Set the text for each row
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
guard let columnIdentifier = tableColumn?.identifier.rawValue else {
return nil
}
var text = ""
// Recall that `item` is the row identiffier
switch (columnIdentifier, item) {
case ("keyColumn", let item as String):
switch item {
case "name":
text = "Name"
case "age":
text = "Age"
case "birthPlace":
text = "Birth Place"
case "birthDate":
text = "Birth Date"
case "hobbies":
text = "Hobbies"
default:
break
}
case ("keyColumn", _):
// Remember that we identified the hobby sub-rows differently
if let (key, index) = item as? (String, Int), key == "hobbies" {
text = person.hobbies[index]
}
case ("valueColumn", let item as String):
switch item {
case "name":
text = person.name
case "age":
text = "\(person.age)"
case "birthPlace":
text = person.birthPlace
case "birthDate":
text = "\(person.birthDate)"
default:
break
}
default:
text = ""
}
let cellIdentifier = NSUserInterfaceItemIdentifier("outlineViewCell")
let cell = outlineView.makeView(withIdentifier: cellIdentifier, owner: self) as! NSTableCellView
cell.textField!.stringValue = text
return cell
}
}
结果
2。更精简的方法
按照#1 设置您的故事板。然后将以下代码复制并粘贴到您的视图控制器中:
import Cocoa
/// The data Model
struct Person {
var name: String
var age: Int
var birthPlace: String
var birthDate: Date
var hobbies: [String]
}
/// Representation of a row in the outline view
struct OutlineViewRow {
var key: String
var value: Any?
var children = [OutlineViewRow]()
static func rowsFrom( person: Person) -> [OutlineViewRow] {
let hobbiesChildren = person.hobbies.map { OutlineViewRow(key: [=11=]) }
return [
OutlineViewRow(key: "Age", value: person.age),
OutlineViewRow(key: "Birth Place", value: person.birthPlace),
OutlineViewRow(key: "Birth Date", value: person.birthDate),
OutlineViewRow(key: "Hobbies", children: hobbiesChildren)
]
}
}
/// A listing of all available columns in the outline view.
///
/// Since repeating string literals is error prone, we define them in a single location here.
/// The literals must match the column identifiers in the Story Board
enum OutlineViewColumn: String {
case key = "keyColumn"
case value = "valueColumn"
init?(tableColumn: NSTableColumn) {
self.init(rawValue: tableColumn.identifier.rawValue)
}
}
class ViewController: NSViewController {
@IBOutlet weak var outlineView: NSOutlineView!
let person = Person(name: "Harry Watson", age: 99, birthPlace: "Westminster",
birthDate: DateComponents(calendar: .current, year: 1985, month: 1, day: 1).date!,
hobbies: ["Tennis", "Piano"])
var rows = [OutlineViewRow]()
override func viewDidLoad() {
super.viewDidLoad()
self.rows = OutlineViewRow.rowsFrom(person: self.person)
outlineView.dataSource = self
outlineView.delegate = self
}
}
extension ViewController: NSOutlineViewDataSource, NSOutlineViewDelegate {
/// Return the item representing each row
/// If item == nil, it is the root of the outline view and is invisible
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
switch item {
case nil:
return self.rows[index]
case let row as OutlineViewRow:
return row.children[index]
default:
return NSNull()
}
}
/// Return the number of children for each row
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
switch item {
case nil:
return self.rows.count
case let row as OutlineViewRow:
return row.children.count
default:
return 0
}
}
/// Determine if the row is expandable
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
switch item {
case let row as OutlineViewRow:
return !row.children.isEmpty
default:
return false
}
}
/// Return content of the cell
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
guard let row = item as? OutlineViewRow,
let column = OutlineViewColumn(tableColumn: tableColumn!)
else {
fatalError("Invalid row and column combination")
}
let text: String
switch column {
case .key:
text = row.key
case .value:
text = row.value == nil ? "" : "\(row.value!)"
}
let identifier = NSUserInterfaceItemIdentifier("outlineViewCell")
let view = outlineView.makeView(withIdentifier: identifier, owner: self) as! NSTableCellView
view.textField?.stringValue = text
return view
}
}
3。使用 Cocoa 绑定
填充大纲视图的另一种方法是使用 Cocoa 绑定,这可以显着减少您需要编写的代码量。但是,将 Cocoa 绑定视为高级主题。当它起作用时,就像变魔术一样,但当它不起作用时,就很难修复。 Cocoa 绑定在 iOS 上不可用。
代码
对于这个例子,让我们提高赌注 NSOutlineView
显示多人的详细信息。
// Data Model
struct Person {
var name: String
var age: Int
var birthPlace: String
var birthDate: Date
var hobbies: [String]
}
// A wrapper object that represents a row in the Outline View
// Since Cocoa Binding relies on the Objective-C runtime, we need to mark this
// class with @objcMembers for dynamic dispatch
@objcMembers class OutlineViewRow: NSObject {
var key: String // content of the Key column
var value: Any? // content of the Value column
var children: [OutlineViewRow] // set to an empty array if the row has no children
init(key: String, value: Any?, children: [OutlineViewRow]) {
self.key = key
self.value = value
self.children = children
}
convenience init(person: Person) {
let hobbies = person.hobbies.map { OutlineViewRow(key: [=12=], value: nil, children: []) }
let children = [
OutlineViewRow(key: "Age", value: person.age, children: []),
OutlineViewRow(key: "Birth Place", value: person.birthPlace, children: []),
OutlineViewRow(key: "Birth Date", value: person.birthDate, children: []),
OutlineViewRow(key: "Hobbies", value: nil, children: hobbies)
]
self.init(key: person.name, value: nil, children: children)
}
}
class ViewController: NSViewController {
let people = [
Person(name: "Harry Watson", age: 99, birthPlace: "Westminster",
birthDate: DateComponents(calendar: .current, year: 1985, month: 1, day: 1).date!,
hobbies: ["Tennis", "Piano"]),
Person(name: "Shelock Holmes", age: 164, birthPlace: "London",
birthDate: DateComponents(calendar: .current, year: 1854, month: 1, day: 1).date!,
hobbies: ["Violin", "Chemistry"])
]
@objc lazy var rows = people.map { OutlineViewRow(person: [=12=]) }
override func viewDidLoad() {
super.viewDidLoad()
}
}
界面生成器设置
在你的故事板中:
- 从对象库添加树控制器
- Select 树控制器并打开属性检查器 (
Cmd + Opt + 4
)。将其子键路径设置为 children
.
- 打开绑定检查器 (
Cmd + Opt + 7
) 并如下设置 IB 对象的绑定。
| IB Object | Property | Bind To | Controller Key | Model Key Path |
|-----------------|--------------------|-----------------|-----------------|-------------------|
| Tree Controller | Controller Content | View Controller | | self.rows |
| Outline View | Content | Tree Controller | arrangedObjects | |
| Table View Cell | Value | Table Cell View | | objectValue.key |
| (Key column) | | | | |
| Table View Cell | Value | Table Cell View | | objectValue.value |
| (Value column) | | | | |
(不要混淆 Table View Cell 和 Table Cell View。糟糕的命名,我知道)
结果
您可以使用 DateFormatter
在这两种方法中获得更好的日期输出,但这对这个问题来说不是必需的。
一个清晰的示例,非常适合作为使用 NSOutlineView 的起点。
当我使用更高版本的 Swift 时,我不得不更改
switch (columnIdentifier, item)
到
switch (columnIdentifier.rawValue, item)
.
Interface Builder 也对设置进行了正确的调整
let cell = outlineView.make(withIdentifier: "outlineViewCell", owner: self) as! NSTableCellView
到
let cell = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "outlineViewCell"), owner: self) as! NSTableCellView
我在 Xcode 8 (Swift 3) 中创建 NSOutlineView 时遇到问题。我有一个 plist 文件,其中包含一些我想在 OutlineView 中显示的信息。 plist 文件如下所示(示例):
Root Dictionary *(1 item)
Harry Watson Dictionary *(5 items)*
name String Harry Watson
age Int 99
birthplace String Westminster
birthdate Date 01/01/1000
hobbies Array *(2 items)*
item 0 String Tennis
item 1 String Piano
OutlineView 应该看起来很相似,如下所示:
name Harry Watson
age 99
birthplace Westminster
birthdate 01/01/1000
> hobbies ... (<- this should be expandable)
我已经在 Google 上搜索了 NSOutlineView 教程,但我找到的都是 raywenderlich.com,所以我读了一点,但我认为这并不容易。 所以我想知道您是否可以帮助我完成上面的确切示例并给我一些代码示例,尤其是关于此功能的示例:
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {}
我不知道该写什么。
如果您有任何问题,请告诉我。
在此先致谢并致以诚挚的问候
我发现 Ray Wenderlitch 的教程质量参差不齐。那些假设你对 Swift 一无所知的内部笑话、冗长、一步一步的掌握对我来说太恶心了。这是一个简明教程,涵盖了手动和通过 Cocoa 绑定填充大纲视图的基础知识。
理解的关键 NSOutlineView
是您必须为每一行赋予一个唯一的标识符,可以是字符串、数字或表示该行的对象。 NSOutlineView
称之为 item
。基于此item
,您将查询数据模型以用数据填充大纲视图。
此答案提供了 3 种方法:
- 手动:以最基本的方式自己做所有事情。这是学习如何与
NSOutlineView
交互的一个很好的介绍,但我不建议将其用于生产代码。 - 精简:大纲视图仍然是手动填充,但方法更优雅。这是我自己的生产代码所用的。
- Cocoa 绑定:Mac OS X 的黄金时代留下的一些神奇的东西。虽然很方便,但不是未来的方式。将此视为高级主题
1。手动填充大纲视图
界面生成器设置
我们将使用一个非常简单的 NSOutlineView
,只有两列:Key 和 Value。
Select 第一列并将其标识符更改为 keyColumn
。然后 select 第二列并将其标识符更改为 valueColumn
:
将单元格的标识符设置为 outlineViewCell
。你只需要做一次。
代码
将以下内容复制并粘贴到您的 ViewController.swift
:
// Data model
struct Person {
var name: String
var age: Int
var birthPlace: String
var birthDate: Date
var hobbies: [String]
}
class ViewController: NSViewController {
@IBOutlet weak var outlineView: NSOutlineView!
// I assume you know how load it from a plist so I will skip
// that code and use a constant for simplicity
let person = Person(name: "Harry Watson", age: 99, birthPlace: "Westminster",
birthDate: DateComponents(calendar: .current, year: 1985, month: 1, day: 1).date!,
hobbies: ["Tennis", "Piano"])
let keys = ["name", "age", "birthPlace", "birthDate", "hobbies"]
override func viewDidLoad() {
super.viewDidLoad()
outlineView.dataSource = self
outlineView.delegate = self
}
}
extension ViewController: NSOutlineViewDataSource, NSOutlineViewDelegate {
// You must give each row a unique identifier, referred to as `item` by the outline view
// * For top-level rows, we use the values in the `keys` array
// * For the hobbies sub-rows, we label them as ("hobbies", 0), ("hobbies", 1), ...
// The integer is the index in the hobbies array
//
// item == nil means it's the "root" row of the outline view, which is not visible
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
if item == nil {
return keys[index]
} else if let item = item as? String, item == "hobbies" {
return ("hobbies", index)
} else {
return 0
}
}
// Tell how many children each row has:
// * The root row has 5 children: name, age, birthPlace, birthDate, hobbies
// * The hobbies row has how ever many hobbies there are
// * The other rows have no children
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
if item == nil {
return keys.count
} else if let item = item as? String, item == "hobbies" {
return person.hobbies.count
} else {
return 0
}
}
// Tell whether the row is expandable. The only expandable row is the Hobbies row
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
if let item = item as? String, item == "hobbies" {
return true
} else {
return false
}
}
// Set the text for each row
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
guard let columnIdentifier = tableColumn?.identifier.rawValue else {
return nil
}
var text = ""
// Recall that `item` is the row identiffier
switch (columnIdentifier, item) {
case ("keyColumn", let item as String):
switch item {
case "name":
text = "Name"
case "age":
text = "Age"
case "birthPlace":
text = "Birth Place"
case "birthDate":
text = "Birth Date"
case "hobbies":
text = "Hobbies"
default:
break
}
case ("keyColumn", _):
// Remember that we identified the hobby sub-rows differently
if let (key, index) = item as? (String, Int), key == "hobbies" {
text = person.hobbies[index]
}
case ("valueColumn", let item as String):
switch item {
case "name":
text = person.name
case "age":
text = "\(person.age)"
case "birthPlace":
text = person.birthPlace
case "birthDate":
text = "\(person.birthDate)"
default:
break
}
default:
text = ""
}
let cellIdentifier = NSUserInterfaceItemIdentifier("outlineViewCell")
let cell = outlineView.makeView(withIdentifier: cellIdentifier, owner: self) as! NSTableCellView
cell.textField!.stringValue = text
return cell
}
}
结果
2。更精简的方法
按照#1 设置您的故事板。然后将以下代码复制并粘贴到您的视图控制器中:
import Cocoa
/// The data Model
struct Person {
var name: String
var age: Int
var birthPlace: String
var birthDate: Date
var hobbies: [String]
}
/// Representation of a row in the outline view
struct OutlineViewRow {
var key: String
var value: Any?
var children = [OutlineViewRow]()
static func rowsFrom( person: Person) -> [OutlineViewRow] {
let hobbiesChildren = person.hobbies.map { OutlineViewRow(key: [=11=]) }
return [
OutlineViewRow(key: "Age", value: person.age),
OutlineViewRow(key: "Birth Place", value: person.birthPlace),
OutlineViewRow(key: "Birth Date", value: person.birthDate),
OutlineViewRow(key: "Hobbies", children: hobbiesChildren)
]
}
}
/// A listing of all available columns in the outline view.
///
/// Since repeating string literals is error prone, we define them in a single location here.
/// The literals must match the column identifiers in the Story Board
enum OutlineViewColumn: String {
case key = "keyColumn"
case value = "valueColumn"
init?(tableColumn: NSTableColumn) {
self.init(rawValue: tableColumn.identifier.rawValue)
}
}
class ViewController: NSViewController {
@IBOutlet weak var outlineView: NSOutlineView!
let person = Person(name: "Harry Watson", age: 99, birthPlace: "Westminster",
birthDate: DateComponents(calendar: .current, year: 1985, month: 1, day: 1).date!,
hobbies: ["Tennis", "Piano"])
var rows = [OutlineViewRow]()
override func viewDidLoad() {
super.viewDidLoad()
self.rows = OutlineViewRow.rowsFrom(person: self.person)
outlineView.dataSource = self
outlineView.delegate = self
}
}
extension ViewController: NSOutlineViewDataSource, NSOutlineViewDelegate {
/// Return the item representing each row
/// If item == nil, it is the root of the outline view and is invisible
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
switch item {
case nil:
return self.rows[index]
case let row as OutlineViewRow:
return row.children[index]
default:
return NSNull()
}
}
/// Return the number of children for each row
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
switch item {
case nil:
return self.rows.count
case let row as OutlineViewRow:
return row.children.count
default:
return 0
}
}
/// Determine if the row is expandable
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
switch item {
case let row as OutlineViewRow:
return !row.children.isEmpty
default:
return false
}
}
/// Return content of the cell
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
guard let row = item as? OutlineViewRow,
let column = OutlineViewColumn(tableColumn: tableColumn!)
else {
fatalError("Invalid row and column combination")
}
let text: String
switch column {
case .key:
text = row.key
case .value:
text = row.value == nil ? "" : "\(row.value!)"
}
let identifier = NSUserInterfaceItemIdentifier("outlineViewCell")
let view = outlineView.makeView(withIdentifier: identifier, owner: self) as! NSTableCellView
view.textField?.stringValue = text
return view
}
}
3。使用 Cocoa 绑定
填充大纲视图的另一种方法是使用 Cocoa 绑定,这可以显着减少您需要编写的代码量。但是,将 Cocoa 绑定视为高级主题。当它起作用时,就像变魔术一样,但当它不起作用时,就很难修复。 Cocoa 绑定在 iOS 上不可用。
代码
对于这个例子,让我们提高赌注 NSOutlineView
显示多人的详细信息。
// Data Model
struct Person {
var name: String
var age: Int
var birthPlace: String
var birthDate: Date
var hobbies: [String]
}
// A wrapper object that represents a row in the Outline View
// Since Cocoa Binding relies on the Objective-C runtime, we need to mark this
// class with @objcMembers for dynamic dispatch
@objcMembers class OutlineViewRow: NSObject {
var key: String // content of the Key column
var value: Any? // content of the Value column
var children: [OutlineViewRow] // set to an empty array if the row has no children
init(key: String, value: Any?, children: [OutlineViewRow]) {
self.key = key
self.value = value
self.children = children
}
convenience init(person: Person) {
let hobbies = person.hobbies.map { OutlineViewRow(key: [=12=], value: nil, children: []) }
let children = [
OutlineViewRow(key: "Age", value: person.age, children: []),
OutlineViewRow(key: "Birth Place", value: person.birthPlace, children: []),
OutlineViewRow(key: "Birth Date", value: person.birthDate, children: []),
OutlineViewRow(key: "Hobbies", value: nil, children: hobbies)
]
self.init(key: person.name, value: nil, children: children)
}
}
class ViewController: NSViewController {
let people = [
Person(name: "Harry Watson", age: 99, birthPlace: "Westminster",
birthDate: DateComponents(calendar: .current, year: 1985, month: 1, day: 1).date!,
hobbies: ["Tennis", "Piano"]),
Person(name: "Shelock Holmes", age: 164, birthPlace: "London",
birthDate: DateComponents(calendar: .current, year: 1854, month: 1, day: 1).date!,
hobbies: ["Violin", "Chemistry"])
]
@objc lazy var rows = people.map { OutlineViewRow(person: [=12=]) }
override func viewDidLoad() {
super.viewDidLoad()
}
}
界面生成器设置
在你的故事板中:
- 从对象库添加树控制器
- Select 树控制器并打开属性检查器 (
Cmd + Opt + 4
)。将其子键路径设置为children
. - 打开绑定检查器 (
Cmd + Opt + 7
) 并如下设置 IB 对象的绑定。
| IB Object | Property | Bind To | Controller Key | Model Key Path |
|-----------------|--------------------|-----------------|-----------------|-------------------|
| Tree Controller | Controller Content | View Controller | | self.rows |
| Outline View | Content | Tree Controller | arrangedObjects | |
| Table View Cell | Value | Table Cell View | | objectValue.key |
| (Key column) | | | | |
| Table View Cell | Value | Table Cell View | | objectValue.value |
| (Value column) | | | | |
(不要混淆 Table View Cell 和 Table Cell View。糟糕的命名,我知道)
结果
您可以使用 DateFormatter
在这两种方法中获得更好的日期输出,但这对这个问题来说不是必需的。
一个清晰的示例,非常适合作为使用 NSOutlineView 的起点。
当我使用更高版本的 Swift 时,我不得不更改
switch (columnIdentifier, item)
到
switch (columnIdentifier.rawValue, item)
.
Interface Builder 也对设置进行了正确的调整
let cell = outlineView.make(withIdentifier: "outlineViewCell", owner: self) as! NSTableCellView
到
let cell = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "outlineViewCell"), owner: self) as! NSTableCellView