Swift 中基于视图的 NSTableView 不显示内容
View-based NSTableView in Swift does not show content
我试图在基于视图的 NSTableView 中显示一个非常基本的数据结构。经过几个小时的搜索,我终于找到了大部分 table 已设置的地步。行数是正确的,我知道对所有单元格都执行了 func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn?, row: Int)
方法,因为它在控制台中打印了正确的数据。不幸的是,table 显示默认值而不是我的数据数组中的 String
s。
代码如下:
ViewController.swift
import Cocoa
class ViewController: NSViewController {
@IBOutlet weak var myTableView: NSTableView!
var files = [FilesListData]()
override func viewDidLoad() {
super.viewDidLoad()
self.setupSampleFiles()
}
override func viewDidAppear() {
super.viewDidAppear()
}
func setupSampleFiles() {
let file1 = FilesListData(file: "test.mp3", title: "test")
let file2 = FilesListData(file: "test2.mp3", title: "test 2")
self.files = [file1, file2]
}
}
extension ViewController: NSTableViewDataSource {
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
return self.files.count
}
func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn?, row: Int) -> AnyObject? {
let cellView: NSTableCellView = tableView.makeViewWithIdentifier((tableColumn!.identifier), owner: self) as! NSTableCellView
let fileData = self.files[row]
if tableColumn!.identifier == "fileCol" {
cellView.textField!.stringValue = fileData.file
} else if tableColumn!.identifier == "titleCol" {
cellView.textField!.stringValue = fileData.title
} else {
cellView.textField!.stringValue = "N/A"
}
return cellView
}
}
extension ViewController: NSTableViewDelegate {
}
FilesListData.swift
import Foundation
class FilesListData: NSObject {
var file: String
var title: String
override init() {
self.file = String()
self.title = String()
}
init(file: String, title: String) {
self.file = file
self.title = title
}
}
一些注意事项:在情节提要中,我确实为 dataSource
和 delegate
创建了一个出口到视图控制器。我的前两列的标识符设置为 "fileCol" 和 "titleCol"。该项目编译并运行,没有任何错误或警告。下面是我的故事板的一些截图。
看来您混淆了两种不同的方法。 数据源方法tableView(_:objectValueForTableColumn:row:)
用于return数据对象,而不是单元格视图。 委托方法tableView(_:viewForTableColumn:row:)
用于return单元格视图,可能配置了适合列和行的数据。
因此,您可以将前者重写为:
func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn?, row: Int) -> AnyObject? {
let fileData = self.files[row]
if tableColumn!.identifier == "fileCol" {
return fileData.file
} else if tableColumn!.identifier == "titleCol" {
return fileData.title
}
return "N/A"
}
请注意,它不会尝试创建或return 视图。
这是第一步。 table 视图将从该方法中获取您 return 的对象值,并将其分配给您的 NSTableCellView
的 objectValue
属性重新用作您的单元格视图。然而,就其本身而言,它没有任何作用。
有几种不同的方法可以使该对象值实际显示在您的单元格视图中:
- 不要使用
NSTableCellView
s,每个都包含一个文本字段,作为您的单元格视图,而是使用文本字段本身。由于 NSTextField
也有一个 objectValue
属性(继承自 NSControl
),table 视图可以在其上设置对象值并显示您的内容。缺点是您的单元格视图只是一个视图。如果您想在每个单元格中使用多个视图,则不能使用这种方法。
- 继续使用
NSTableCellView
s 作为您的单元格视图,就像您现在所做的那样。使用键路径 objectValue
将文本字段的值绑定绑定到包含的 NSTableCellView
。这样,当 table 设置 NSTableCellView
的 objectValue
时,它会自动传播到文本字段。
- 实施
tableView(_:viewForTableColumn:row:)
通过直接设置文本字段属性的值来配置单元格视图。基本上,这看起来像您为 tableView(_:objectValueForTableColumn:row:)
实现的主体。这实际上使 tableView(_:objectValueForTableColumn:row:)
的实现变得多余。您需要实现它,table 视图将调用它并使用结果来设置单元格视图的 objectValue
,但这不会影响单元格视图中的文本字段。
- 使用
NSTableCellView
的自定义子类,将 didSet
属性 观察者添加到 objectValue
属性。可以根据需要将新值转发给子视图。
就个人而言,我使用绑定方法。
此外,您应该考虑 returning 整个 fileData
对象,而不是 tableView(_:objectValueForTableColumn:row:)
的其中一个属性的值。所以,tableView(_:objectValueForTableColumn:row:)
对列不敏感,只对行敏感。每个 NSTableCellView
的 objectValue
将是相应的 FilesListData
个实例。
然后,文本字段与其封闭单元格视图的绑定将调整为 select FilesListData
的适当 属性。例如,一列中的文本字段将绑定到它的封闭 NSTableCellView
,键路径为 objectValue.file
。在另一列中,它将使用 objectValue.title
.
的关键路径
如果您的单元格视图最终变得更加复杂且包含多个视图,这将特别有用。每个视图都可以访问 FilesListData
对象的任何属性。使用您当前 return 仅将一个 属性 作为单元格对象值的方法,那么视图将只能使用该值。
我试图在基于视图的 NSTableView 中显示一个非常基本的数据结构。经过几个小时的搜索,我终于找到了大部分 table 已设置的地步。行数是正确的,我知道对所有单元格都执行了 func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn?, row: Int)
方法,因为它在控制台中打印了正确的数据。不幸的是,table 显示默认值而不是我的数据数组中的 String
s。
代码如下:
ViewController.swift
import Cocoa
class ViewController: NSViewController {
@IBOutlet weak var myTableView: NSTableView!
var files = [FilesListData]()
override func viewDidLoad() {
super.viewDidLoad()
self.setupSampleFiles()
}
override func viewDidAppear() {
super.viewDidAppear()
}
func setupSampleFiles() {
let file1 = FilesListData(file: "test.mp3", title: "test")
let file2 = FilesListData(file: "test2.mp3", title: "test 2")
self.files = [file1, file2]
}
}
extension ViewController: NSTableViewDataSource {
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
return self.files.count
}
func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn?, row: Int) -> AnyObject? {
let cellView: NSTableCellView = tableView.makeViewWithIdentifier((tableColumn!.identifier), owner: self) as! NSTableCellView
let fileData = self.files[row]
if tableColumn!.identifier == "fileCol" {
cellView.textField!.stringValue = fileData.file
} else if tableColumn!.identifier == "titleCol" {
cellView.textField!.stringValue = fileData.title
} else {
cellView.textField!.stringValue = "N/A"
}
return cellView
}
}
extension ViewController: NSTableViewDelegate {
}
FilesListData.swift
import Foundation
class FilesListData: NSObject {
var file: String
var title: String
override init() {
self.file = String()
self.title = String()
}
init(file: String, title: String) {
self.file = file
self.title = title
}
}
一些注意事项:在情节提要中,我确实为 dataSource
和 delegate
创建了一个出口到视图控制器。我的前两列的标识符设置为 "fileCol" 和 "titleCol"。该项目编译并运行,没有任何错误或警告。下面是我的故事板的一些截图。
看来您混淆了两种不同的方法。 数据源方法tableView(_:objectValueForTableColumn:row:)
用于return数据对象,而不是单元格视图。 委托方法tableView(_:viewForTableColumn:row:)
用于return单元格视图,可能配置了适合列和行的数据。
因此,您可以将前者重写为:
func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn?, row: Int) -> AnyObject? {
let fileData = self.files[row]
if tableColumn!.identifier == "fileCol" {
return fileData.file
} else if tableColumn!.identifier == "titleCol" {
return fileData.title
}
return "N/A"
}
请注意,它不会尝试创建或return 视图。
这是第一步。 table 视图将从该方法中获取您 return 的对象值,并将其分配给您的 NSTableCellView
的 objectValue
属性重新用作您的单元格视图。然而,就其本身而言,它没有任何作用。
有几种不同的方法可以使该对象值实际显示在您的单元格视图中:
- 不要使用
NSTableCellView
s,每个都包含一个文本字段,作为您的单元格视图,而是使用文本字段本身。由于NSTextField
也有一个objectValue
属性(继承自NSControl
),table 视图可以在其上设置对象值并显示您的内容。缺点是您的单元格视图只是一个视图。如果您想在每个单元格中使用多个视图,则不能使用这种方法。 - 继续使用
NSTableCellView
s 作为您的单元格视图,就像您现在所做的那样。使用键路径objectValue
将文本字段的值绑定绑定到包含的NSTableCellView
。这样,当 table 设置NSTableCellView
的objectValue
时,它会自动传播到文本字段。 - 实施
tableView(_:viewForTableColumn:row:)
通过直接设置文本字段属性的值来配置单元格视图。基本上,这看起来像您为tableView(_:objectValueForTableColumn:row:)
实现的主体。这实际上使tableView(_:objectValueForTableColumn:row:)
的实现变得多余。您需要实现它,table 视图将调用它并使用结果来设置单元格视图的objectValue
,但这不会影响单元格视图中的文本字段。 - 使用
NSTableCellView
的自定义子类,将didSet
属性 观察者添加到objectValue
属性。可以根据需要将新值转发给子视图。
就个人而言,我使用绑定方法。
此外,您应该考虑 returning 整个 fileData
对象,而不是 tableView(_:objectValueForTableColumn:row:)
的其中一个属性的值。所以,tableView(_:objectValueForTableColumn:row:)
对列不敏感,只对行敏感。每个 NSTableCellView
的 objectValue
将是相应的 FilesListData
个实例。
然后,文本字段与其封闭单元格视图的绑定将调整为 select FilesListData
的适当 属性。例如,一列中的文本字段将绑定到它的封闭 NSTableCellView
,键路径为 objectValue.file
。在另一列中,它将使用 objectValue.title
.
如果您的单元格视图最终变得更加复杂且包含多个视图,这将特别有用。每个视图都可以访问 FilesListData
对象的任何属性。使用您当前 return 仅将一个 属性 作为单元格对象值的方法,那么视图将只能使用该值。