代码之家  ›  专栏  ›  技术社区  ›  RickshawBoy

在Swift中仅对NSTableView的一列使用类型选择

  •  1
  • RickshawBoy  · 技术社区  · 7 年前

    我在XCode 9.3中工作,Swift正在为MacOS开发一个应用程序。

    我创建了一个当前有两列的表。我想使用只作用于一列的类型选择,这样当用户键入前几个字母时,它只在第一列中进行选择,而不在第二列中进行选择。

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

    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
      }
    
    }
    
    1 回复  |  直到 5 年前
        1
  •  1
  •   Charles Srstka    7 年前

    您的问题有两方面:

    首先,需要为每个表列设置标识符。可以通过在Interface Builder中选择列,转到Identity Inspector,并将其设置为某个字符串值来完成此操作:

    enter image description here

    完成后,创建一些静态属性来引用这些标识符(这不是 严格地 必要的,因为你当然可以 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:) ,用于在事件级别捕获这些内容(即,用户刚刚按下一个键。我是否应该调用类型选择系统?),使用 tableView(:typeSelectStringFor:row:) 。此方法允许您为表中的每个行/列组合返回给定给类型选择机制的字符串。对于希望type select忽略的那些,只需返回 nil ;否则,返回您希望必须键入的字符串以选择特定行。比如:

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