尝试使用 NSTableView

Trying to use NSTableView

我是一名 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" 的单元格? (在 运行 时,没有包含此屏幕截图)

如果您在 tableView(_:viewFor:row:) 方法中添加日志,您会发现它从未被调用过。为什么会这样,你可能想知道?好吧,这很复杂:

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

要解决您眼前的问题,请将 NSTableViewDelegate 添加到您的扩展中,并确保您的数据源在 Interface Builder 中设置为委托。但是,在制作 Mac 应用程序时,我发现使用 Cocoa 绑定来填充 table 视图更容易,因为您可以获得很多功能 "for free",例如按列自动排序,type-ahead selection,以及 selection 管理。为此,请按照下列步骤操作:

1) 确保 object 上的数组 属性 同时标有 @objcdynamic 关键字,并且 class数组中包含一个 NSObject subclass,其相关属性也标记为 @objcdynamic:

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-compliant,所以我们不必自己做(这是必需的,因为Cocoa 绑定是基于 KVO 构建的。

2) 在 Interface Builder 中创建一个 Array Controller,然后在 Bindings Inspector 中,将 Array Controller 的 "Model Key Path" 设置为您的 属性:

3) 现在 select 您的 table 视图,并在其绑定检查器中,将其内容、Select离子索引和排序描述符绑定到 arrangedObjectsselectionIndexessortDescriptors,每个 "Model Key Path" 留空:

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

5) 最后,select table 列并将其排序键设置为属性检查器中的 属性 名称("Selector" 字段允许您自定义在 object 上调用什么方法来对它们进行排序;我喜欢使用 localizedStandardCompare: 对字符串进行 case-insensitive 排序,但对于大多数其他类型,您可以将其保留为默认值) :

瞧瞧:

这在 Interface Builder 中看起来似乎很麻烦,但最后我们几乎没有代码就设置了整个 table。而且,我们为您的用户提供了一个非常灵活的 UI,以及一系列免费的功能,我最喜欢的是通过单击 headers:

自动排序

这样做的好处不仅在于您不必自己操心 re-sorting 数组,而且它甚至不会打乱原始数组的顺序;更改仅用于显示目的。这里的数组还是["Foo", "Bar"].

NSArrayController 的另一个非常酷的功能是它还会为您管理 selection。例如,如果你的 table 视图是侧边栏,你可以将右侧的另一个视图绑定到数组控制器中的 selected object,这样你就可以轻松实现类似Mail.app 的窗格查看器,包括在用户 select 一次超过一个 object 时指定要使用的占位符等内容。真的很光滑