Swift 中基于视图的 NSTableView 不显示内容

View-based NSTableView in Swift does not show content

我试图在基于视图的 NSTableView 中显示一个非常基本的数据结构。经过几个小时的搜索,我终于找到了大部分 table 已设置的地步。行数是正确的,我知道对所有单元格都执行了 func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn?, row: Int) 方法,因为它在控制台中打印了正确的数据。不幸的是,table 显示默认值而不是我的数据数组中的 Strings。

代码如下:

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

一些注意事项:在情节提要中,我确实为 dataSourcedelegate 创建了一个出口到视图控制器。我的前两列的标识符设置为 "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 的对象值,并将其分配给您的 NSTableCellViewobjectValue 属性重新用作您的单元格视图。然而,就其本身而言,它没有任何作用。

有几种不同的方法可以使该对象值实际显示在您的单元格视图中:

  • 不要使用 NSTableCellViews,每个都包含一个文本字段,作为您的单元格视图,而是使用文本字段本身。由于 NSTextField 也有一个 objectValue 属性(继承自 NSControl),table 视图可以在其上设置对象值并显示您的内容。缺点是您的单元格视图只是一个视图。如果您想在每个单元格中使用多个视图,则不能使用这种方法。
  • 继续使用 NSTableCellViews 作为您的单元格视图,就像您现在所做的那样。使用键路径 objectValue 将文本字段的值绑定绑定到包含的 NSTableCellView。这样,当 table 设置 NSTableCellViewobjectValue 时,它会自动传播到文本字段。
  • 实施 tableView(_:viewForTableColumn:row:) 通过直接设置文本字段属性的值来配置单元格视图。基本上,这看起来像您为 tableView(_:objectValueForTableColumn:row:) 实现的主体。这实际上使 tableView(_:objectValueForTableColumn:row:) 的实现变得多余。您需要实现它,table 视图将调用它并使用结果来设置单元格视图的 objectValue,但这不会影响单元格视图中的文本字段。
  • 使用 NSTableCellView 的自定义子类,将 didSet 属性 观察者添加到 objectValue 属性。可以根据需要将新值转发给子视图。

就个人而言,我使用绑定方法。

此外,您应该考虑 returning 整个 fileData 对象,而不是 tableView(_:objectValueForTableColumn:row:) 的其中一个属性的值。所以,tableView(_:objectValueForTableColumn:row:) 对列不敏感,只对行敏感。每个 NSTableCellViewobjectValue 将是相应的 FilesListData 个实例。

然后,文本字段与其封闭单元格视图的绑定将调整为 select FilesListData 的适当 属性。例如,一列中的文本字段将绑定到它的封闭 NSTableCellView,键路径为 objectValue.file。在另一列中,它将使用 objectValue.title.

的关键路径

如果您的单元格视图最终变得更加复杂且包含多个视图,这将特别有用。每个视图都可以访问 FilesListData 对象的任何属性。使用您当前 return 仅将一个 属性 作为单元格对象值的方法,那么视图将只能使用该值。