在 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
}
}
我正在 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
}
}