如果您在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 绑定来填充表格视图更容易,因为您可以“免费”获得许多功能,例如按列自动排序、预先输入选择和选择管理。为此,请按以下步骤操作:
1) 确保对象上的数组属性同时标记了@objc 和dynamic 关键字,并且数组中包含的类是NSObject 子类,并且还标记了其相关属性@objc 和 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) 现在选择您的表格视图,并在其绑定检查器中,将其内容、选择索引和排序描述符分别绑定到 arrangedObjects、selectionIndexes 和 sortDescriptors,将“模型键路径”留空每个:
4) 在表格视图的单元格中选择您的文本字段,转到其绑定检查器,并将其绑定到表格单元格视图,模型键路径为objectValue.,然后是您要查看的属性名称细胞:
5) 最后,选择表格列并将其排序键设置为属性检查器中的属性名称(“选择器”字段可让您自定义调用对象的方法以对其进行排序;我喜欢使用 @ 987654350@ 用于字符串进行不区分大小写的排序,但对于大多数其他类型,您可以将其保留为默认值):
等等:
这在 Interface Builder 中可能看起来很麻烦,但在它结束时,我们几乎没有代码就设置了整个表。而且,我们为您的用户提供了一个非常漂亮的用户界面,以及一系列免费的功能,我最喜欢的是通过单击标题自动排序:
这样做的好处不仅在于您不必自己重新排序数组,而且它甚至不会弄乱原始数组的顺序;更改仅用于显示目的。这里的数组还是["Foo", "Bar"]。
NSArrayController 的另一个非常酷的功能是它也会为您管理选择。例如,如果您的表格视图是侧边栏,您可以将右侧的另一个视图绑定到数组控制器中的选定对象,这样您就可以轻松实现 Mail.app 的窗格查看器之类的东西,包括指定要使用的占位符之类的东西如果用户一次选择多个对象。真的很流畅。