为什么带有 autosaveExpandedItems true 的基于视图的 NSOutlineView 会忽略 reloadData 时的扩展?

Sil*_* St 2 macos nsoutlineview swift

我使用 NSOutlineView 自动保存展开状态。如果我在数据源更新时手动重新加载数据,则func outlineView(_ outlineView: NSOutlineView, itemForPersistentObject object: Any) -> Any?不再调用数据源方法,并且每个单元格都会崩溃。知道为什么会发生这种情况吗?

尝试使用 nil send 作为参数重新加载项目,但仍然没有效果。

我用它来持久保存扩展行:

func outlineView(_ outlineView: NSOutlineView, persistentObjectForItem item: Any?) -> Any? {
    return NSKeyedArchiver.archivedData(withRootObject: item)
}

func outlineView(_ outlineView: NSOutlineView, itemForPersistentObject object: Any) -> Any? {
    guard let data = object as? Data,
        let item = NSKeyedUnarchiver.unarchiveObject(with: data) as? Category else { return nil }
    let foundItem = recursiveSearch(for: item, in: viewModel.dataSource.value)
    return foundItem
}
Run Code Online (Sandbox Code Playgroud)

这是重新加载数据:

  viewModel.dataSource.subscribe(onNext: { [weak self] _ in
            self?.outlineView.reloadData()
  }).disposed(by: disposeBag)
Run Code Online (Sandbox Code Playgroud)

zrz*_*zka 5

恕我直言,自动保存是一种不成熟的功能,它无法按预期工作。换句话说,它的实现方式是在您的应用程序启动时(仅一次)恢复状态,然后您就可以自己了。

\n

利用outlineViewItemDidExpand(_:)&实现您自己的一个outlineViewItemDidCollapse(_:)(特别是当我们重新加载时,...)。

\n

如果您不想实现自定义自动保存,可以使用一些技巧。但我不会依赖他们。

\n

第一个技巧 - 告诉 NSOutlineView 重新加载持久状态

\n

NSOutlineView继承自NSTableView并且autosaveName属性文档说:

\n
\n

如果将此属性的值更改为新名称,表会读取所有已保存的信息,并设置此表 view\xe2\x80\x99s 列的顺序和宽度以进行匹配。将名称设置为nil将从用户默认值中删除任何先前存储的状态。

\n
\n

这里不准确的是 - 将其设置为nil不会删除以前存储的扩展项目状态NSOutlineView。我们可以使用它来强制重新NSOutlineView加载扩展项目状态:

\n
class ViewController: NSViewController, NSOutlineViewDelegate, NSOutlineViewDataSource {\n    @IBOutlet var outlineView: NSOutlineView!\n    // It\'s for testing, to demonstrate the persistent state reloading\n    private var doNotLoad = true\n\n    override func viewDidAppear() {\n        super.viewDidAppear()\n        \n        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {\n            self.doNotLoad = false\n            \n            let autosaveName = self.outlineView.autosaveName\n            self.outlineView.autosaveName = nil            \n            self.outlineView.reloadData()\n            self.outlineView.autosaveName = autosaveName\n        }\n    }\n\n    func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {\n        if (doNotLoad) {\n            return 0\n        }\n        return item == nil ? data.count : (item as! Node).children.count\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

如果您想遵守文档,请不要使用nil和设置一些假名称。但我希望一旦 bug 被修复,如果我们更改autosaveName或将其设置为 ,则持久状态将被删除nil

\n

第二招——加载并扩展自己

\n

假设您有以下Node课程:

\n
class Node {\n    let id: Int\n    let children: [Node]\n    // ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n

并且您的数据源实现:

\n
func outlineView(_ outlineView: NSOutlineView, persistentObjectForItem item: Any?) -> Any? {\n    (item as! Node).id\n}\n\nfunc outlineView(_ outlineView: NSOutlineView, itemForPersistentObject object: Any) -> Any? {\n    guard let id = object as? Int else { return nil }\n    return data.firstNode { $0.id == id }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

firstNode与这个问题无关,但这里是实现(因为它在代码中提到):

\n
extension Array where Self.Element == Node {\n    // Search for a node (recursively) until a matching element is found\n    func firstNode(where predicate: (Element) throws -> Bool) rethrows -> Element? {\n        for element in self {\n            if try predicate(element) {\n                return element\n            }\n            if let matched = try element.children.firstNode(where: predicate) {\n                return matched\n            }\n        }\n        return nil\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

然后你可以reloadData自己展开所有项目:

\n
outlineView.reloadData()\nif outlineView.autosaveExpandedItems,\n   let autosaveName = outlineView.autosaveName,\n   let persistentObjects = UserDefaults.standard.array(forKey: "NSOutlineView Items \\(autosaveName)"),\n   let itemIds = persistentObjects as? [Int] {\n    \n    itemIds.forEach {\n        let item = outlineView.dataSource?.outlineView?(self.outlineView, itemForPersistentObject: $0)\n        self.outlineView.expandItem(item)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n