NSTableView - 使用 NSSortDescriptor 排序 collection 的更好解决方案

NSTableView - Better solution for sorting collection with NSSortDescriptor

我有一个 NSTableView,其中有 2 列绑定了自定义类型 (SelectedFiles) 数组,如 File NameFile Path,单击 header 后,我想要它为了按升序/降序对数据进行排序,我用 NSSortDescriptor:

尝试了这些代码
class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        let fileNameSortDescriptor = NSSortDescriptor(key: "fileName", ascending: true, selector: #selector(NSString.localizedStandardCompare(_:)))
        tableView.tableColumns[0].sortDescriptorPrototype = fileNameSortDescriptor
        // other codes
    }
}

extension ViewController: NSTableViewDataSource, NSTableViewDelegate {

    func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
        let selectedFilesArray = NSMutableArray(array: selectedFiles)
        selectedFilesArray.sort(using: tableView.sortDescriptors) // Signal SIGABRT
        selectedFiles = selectedFilesArray as! [SelectedFiles]
        tableView.reloadData()
    }
}

我对 table 视图中数据的自定义 collection:

struct SelectedFiles: CustomStringConvertible {
    let fileName: String
    let filePath: String
    var description: String {
        return "\(fileName) at path \(filePath)"
    }
}

var selectedFiles: [SelectedFiles] = []

事实证明它根本不起作用,如果我的代码有任何问题或者我遗漏了什么,请 IDK。

所以,我想到了这个尴尬的解决方案:

var tableViewSortingOrder = ComparisonResult.orderedAscending

extension ViewController: NSTableViewDataSource, NSTableViewDelegate {

    func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
        switch tableViewSortingOrder {
        case .orderedAscending:
            tableViewSortingOrder = .orderedDescending
            selectedFiles.sort { (previous, next) -> Bool in
                return previous.fileName.compare(next.fileName) == tableViewSortingOrder
            }
        default:
            tableViewSortingOrder = .orderedAscending
            selectedFiles.sort { (previous, next) -> Bool in
                return previous.fileName.compare(next.fileName) == tableViewSortingOrder
        }
        tableView.reloadData()
    }
}

在我改用这个解决方案后,它在升序/降序之间快速切换时效果很好。但是,当涉及到删除 collection 中的 objects 时,当我尝试从 collection 和 [=] 中删除多个 objects 时,它会抛出 Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value 45=] 查看一些特定文件。

所以,我在考虑是否应该通过使用 NSSortDescriptor(通过更正我的第一种方法使用 old-fashioned 方法)来改变实现此 header 排序的方法为了摆脱这个问题,我不得不承认我的第二种方式有点尴尬(更像是一个计划C)。

我已经浏览了关于这个主题的多篇 Whosebug 帖子,我尝试了他们所有的方法,尤其是 this one,我没有使用 CoreData,它的解决方案不适合我的情况。

请问有谁能帮忙指路吗?

我从 Apple Developer Site 和其他一些 Whosebug 帖子中找到了 NSTableView 指南,我发现自己是 Swift 4:

的可行解决方案

我在 ViewController class 下的 viewDidLoad() 中将 sortDescriptorPrototype 设置为 fileNameSortDescriptor

class ViewController: NSViewController {
    override func viewDidLoad()
        super.viewDidLoad()
        let fileNameSortDescriptor = NSSortDescriptor(key: "fileName", ascending: true, selector: #selector(NSString.localizedStandardCompare))
        let tableColumn = tableView.tableColumn(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "fileNameColumn"))!
        tableColumn.sortDescriptorPrototype = fileNameSortDescriptor
        // other codes
    }
}

然后我从NSObject添加了一个继承并插入了@objcMembers以防止warning: Object <#object#> of class '<#class#>' does not implement methodSignatureForSelector: -- trouble ahead发生然后在调用selectedFiles.sort(using: tableView.sortDescriptors)时导致Signal SIGABRT(参考: Object X of class Y does not implement methodSignatureForSelector in Swift).

@objcMembers class SelectedFiles: NSObject {
    let fileName: String
    let filePath: String
    override var description: String {
        return "\(fileName) at path \(filePath)"

    init(fileName: String, filePath: String) {
        self.fileName = fileName
        self.filePath = filePath
    }
}

这是 NSTableViewDataSourcetableView(_:sortDescriptorsDidChange:) 的代码:

extension ViewController: NSTableViewDataSource {
    func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
        var selectedFilesArray = NSArray(array: selectedFiles)
        selectedFilesArray = selectedFilesArray.sortedArray(using: tableView.sortDescriptors) as NSArray
        selectedFiles = selectedFilesArray as! [SelectedFiles]
        tableView.reloadData()
    }
}

现在,一切正常。