【问题标题】:Trying to use NSTableView尝试使用 NSTableView
【发布时间】:2017-10-31 11:21:58
【问题描述】:

我是一名 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”的单元格? (在运行时,不包括此截图)

【问题讨论】:

    标签: swift macos nstableview nstableviewcell nstablecolumn


    【解决方案1】:

    如果您在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) 确保对象上的数组属性同时标记了@objcdynamic 关键字,并且数组中包含的类是NSObject 子类,并且还标记了其相关属性@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,因此我们不必自己做(这是必需的,因为 Cocoa Bindings 是基于 KVO 构建的)。

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

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

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

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

    等等:

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

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

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

    【讨论】:

    • 哇非常详细的答案!太谢谢了。不过,您将 NSTableViewDelegate 添加到我的扩展程序的初始答案已经解决了我的问题;-)。谢谢!
    • @RutgerHuijsmans 不过,你应该看看 Cocoa Bindings 的未来;当使用表格视图做复杂的事情时,它真的可以节省很多时间。 :-)
    • @CharlesSrstka 故事板和视图控制器将来会友好绑定吗?
    • @Willeke Apple 没有任何迹象表明他们不会。
    • 目前让绑定跨视图控制器工作并不容易。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多