在 Swift 中使用 NSTableView 仅在一列上使用类型 select

Using type select on only one column with NSTableView in Swift

我正在 XCode 9.3,Swift 开发 MacOS 应用程序。

我创建了一个 table,目前有两列。我想使用仅作用于一列的类型 select,这样当用户键入前几个字母时,它只会在第一列而不是第二列中出现 selects。

Apple 文档在 Objective-C 中有一个示例:https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/TableView/RowSelection/RowSelection.html#//apple_ref/doc/uid/10000026i-CH6-SW1 但我似乎无法将其翻译成 Swift。我在下面包含了 table 代码。任何帮助都将不胜感激。

extension ViewController: NSTableViewDataSource {
  func numberOfRows(in tableView: NSTableView) -> Int {
    return directoryItems?.count ?? 0
  }

  // Sorting.
  func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
    guard let sortDescriptor = tableView.sortDescriptors.first else {
      return
    }

    if let order = Directory.FileOrder(rawValue: sortDescriptor.key!) {
      sortOrder = order
      sortAscending = sortDescriptor.ascending
      reloadFileList()
    }
  }

}

extension ViewController: NSTableViewDelegate {

  fileprivate enum CellIdentifiers {
    static let NameCell = "NameCellID"
    static let TimeCell = "TimeCellID"
    static let SizeCell = "SizeCellID"
  }

  func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {

    var image: NSImage?
    var text: String = ""
    var cellIdentifier: String = ""

    let dateFormatter = DateFormatter()
    dateFormatter.dateStyle = .long
    dateFormatter.timeStyle = .long

    guard let item = directoryItems?[row] else {
      return nil
    }

    if tableColumn == tableView.tableColumns[0] {
      image = item.icon
      text = item.name
      cellIdentifier = CellIdentifiers.NameCell

    } else if tableColumn == tableView.tableColumns[1] {
      let asset = AVAsset(url: item.url as URL)
      let audioTime = asset.duration
      text = audioTime.durationText
      cellIdentifier = CellIdentifiers.TimeCell

    } else if tableColumn == tableView.tableColumns[2] {
      text = item.isFolder ? "--" : sizeFormatter.string(fromByteCount: item.size)
      cellIdentifier = CellIdentifiers.SizeCell
    }

    if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier), owner: nil) as? NSTableCellView {
      cell.textField?.stringValue = text
      cell.imageView?.image = image ?? nil
      return cell
    }

    return nil
  }

}

你的问题是双重的:

首先,您需要为每个 table 列设置一个标识符。您可以通过 select 在 Interface Builder 中编辑该列,转到身份检查器并将其设置为某个字符串值来完成此操作:

完成后,创建一些静态属性来引用这些标识符(这严格不是必需的,因为您当然可以只使用rawValue 以 Objective-C 方式进行纯字符串比较,但新的 Swift 4 标识符是类型安全的,因此是首选)。这样做:

extension ViewController: NSTableViewDelegate {
    private struct TableColumns {
        static let foo = NSUserInterfaceItemIdentifier("Foo")
        static let bar = NSUserInterfaceItemIdentifier("Bar")
    }

    ...

}

现在,您可以使用这些标识符来引用您的列,使用 tableColumn(withIdentifier:)(如果您只需要列号,则可以使用 column(withIdentifier:))。我建议在你引用它们的任何地方都这样做,包括在你的 tableView(:viewFor:row:) 方法中,因为你现在使用 tableView.tableColumns[0] 等等的方式取决于列的顺序,如果用户重新排序它们,可能会导致意外行为。使用标识符将确保您始终查看您认为正在查看的列。

无论如何,一旦设置了标识符,就可以解决第二个问题:您使用了错误的委托方法。而不是 tableView(:shouldTypeSelectFor:searchString:),这意味着在事件级别捕获这些东西(即,用户刚刚按下一个键。我应该调用类型 select 系统吗?),使用 tableView(:typeSelectStringFor:row:).此方法允许您 return 为 table 中的每个 row/column 组合提供类型 selection 机制的字符串。对于那些你想要 type-select 忽略的,只是 return nil;否则,return 您希望必须键入的字符串 select 该特定行。所以,像这样:

func tableView(_ tableView: NSTableView, typeSelectStringFor tableColumn: NSTableColumn?, row: Int) -> String? {
    if tableColumn?.identifier == TableColumns.foo {
        return directoryItems?[row].name
    } else {
        return nil
    }
}