1

我是一名 iOS 开发人员,我正在创建我的第一个 Mac 应用程序。尝试使用 NSTableView 时遇到一些困难。

extension HomeViewController:NSTableViewDataSource{
    func numberOfRows(in tableView: NSTableView) -> Int {
        print(self.customerApplicationList.count) // '1' gets printed here
        return self.customerApplicationList.count
    }

    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?{
        var result:NSTableCellView
        result  = tableView.make(withIdentifier: "firstName", owner: self) as! NSTableCellView
        result.textField?.stringValue = "test"
        return result
    }
}

在此处输入图像描述

为什么没有显示值“test”的单元格?(在运行时,不包括此截图)

4

1 回答 1

2

如果你在你的tableView(_:viewFor:row:)方法中加入一个日志,你会发现它永远不会被调用。为什么会这样,你可能想知道?好吧,这很复杂:

众所周知,AppKit 不是使用 Swift 实现的;它是在 Objective-C 中实现的。Objective-C 是一门非常动态的语言,它允许调用者查询一个对象是否响应某个消息,所以一个对象只需要实现一个类似的方法-tableView:viewForTableColumn:row:,通过 Objective-C 的魔力,AppKit 可以找到方法并调用它。使用 Swift,事情会稍微复杂一些,因为默认情况下,Swift 方法不会暴露给 Objective-C,除非我们通过@objc关键字明确地这样做,如果该方法是一个 Objective-C 超类方法的覆盖,或者如果该方法满足 Objective-C 协议。这些情况中的第三种应该发生在这里,除了事实证明tableView(_:viewFor:row:)实际上属于NSTableViewDelegate,而不是NSTableViewDataSource. 因此,Swift 编译器不会认为您的方法满足任何协议,并且它不会暴露给 Objective-C。所以从 AppKit 的角度来看,就好像你根本没有实现它一样。

要解决您当前的问题,请添加NSTableViewDelegate到您的扩展中,并确保您的数据源在 Interface Builder 中设置为委托。但是,在制作 Mac 应用程序时,我发现使用 Cocoa Bindings 来填充表格视图更容易,因为您可以“免费”获得许多功能,例如按列自动排序、预先输入选择和选择管理。为此,请按照下列步骤操作:

1) 确保对象上的数组属性同时标记了@objcanddynamic关键字,并且数组中包含的类是NSObject子类,并且其相关属性也标记为@objcand dynamic

class Thingy: NSObject {
    @objc dynamic var name: String

    init(name: String) { self.name = name }
}

class MyViewControllerThingy: NSViewController {
    @objc dynamic var myArray: [Thingy] = [Thingy(name: "Foo"), Thingy(name: "Bar")]
}

这确保了 AppKit 可以使用其动态的 Objective-C 魔法来自动使该属性符合 KVO,因此我们不必自己做(这是必需的,因为 Cocoa Bindings 是基于 KVO 构建的)。

2) 在 Interface Builder 中创建一个 Array Controller,并在 Bindings Inspector 中,将 Array Controller 的“Model Key Path”设置为您的属性名称:

在此处输入图像描述

3) 现在选择您的表格视图,并在其绑定检查器中,将其内容、选择索引和排序描述符分别绑定到arrangedObjectsselectionIndexes和 ,并将sortDescriptors每个“模型键路径”留空:

在此处输入图像描述

4)在表格视图的单元格中选择您的文本字段,转到其绑定检查器,并将其绑定到表格单元格视图,模型键路径为objectValue.,然后是您要在单元格中查看的属性名称:

在此处输入图像描述

5)最后,选择表格列并将其排序键设置为属性检查器中的属性名称(“选择器”字段允许您自定义调用对象的方法以对其进行排序;我喜欢使用localizedStandardCompare:字符串来获取不区分大小写的排序,但对于大多数其他类型,您可以将其保留为默认值):

在此处输入图像描述

等等:

在此处输入图像描述

这在 Interface Builder 中可能看起来很麻烦,但在它结束时,我们几乎没有代码就设置了整个表。而且,我们为您的用户提供了一个非常漂亮的 UI,以及一系列免费的功能,我最喜欢的是通过单击标题自动排序:

在此处输入图像描述

这样做的好处不仅在于您不必费心自己重新排序数组,而且它甚至不会弄乱原始数组的顺序;更改仅用于显示目的。这里的数组仍然是["Foo", "Bar"].

另一个非常酷的功能NSArrayController是它也会为您管理选择。例如,如果您的表格视图是侧边栏,您可以将右侧的另一个视图绑定到数组控制器中的选定对象,这样您就可以轻松实现 Mail.app 的窗格查看器之类的东西,包括指定要使用的占位符之类的东西如果用户一次选择多个对象。它真的很光滑。

于 2017-10-31T02:52:47.137 回答