You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何自动展开NSOutlineView目录路径以恢复用户上次访问位置?

解决方案:自动展开目录路径并恢复上次访问位置

针对你提出的两个技术问题,结合你开发的目录展示应用需求,我会一步步给出修改方案,实现自动展开路径和恢复上次访问位置的功能。

1. 先修复DirectoryItem的父节点关联

首先,我们需要给DirectoryItem添加父节点引用,这样才能从任意子节点追溯到顶层父节点。修改你的DirectoryItem类:

class DirectoryItem {
    var name: String
    var url: URL
    var isdir: Bool
    var prev: URL
    weak var parent: DirectoryItem? // 新增:记录父节点,用weak避免循环引用
    
    lazy var isExpandable: Bool = {
        do {
            return try url.resourceValues(forKeys: [.isDirectoryKey]).isDirectory ?? false
        } catch let error as NSError {
            return false
        }
    }()
    
    lazy var childItems: [DirectoryItem] = {
        do {
            let urls = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: [.isDirectoryKey], options: [.skipsHiddenFiles])
            var bb: [DirectoryItem] = []
            // 创建子节点时传入当前节点作为父节点
            let aa = urls.map { DirectoryItem(url: $0, parent: self) }
            for a in aa {
                if(a.isdir) {
                    bb.append(a)
                }
            }
            return bb
        } catch let error as NSError {
            return []
        }
    }()
    
    // 修改初始化方法,支持传入父节点
    init(url: URL, parent: DirectoryItem? = nil) {
        self.url = url
        self.name = url.lastPathComponent
        self.isdir = url.hasDirectoryPath
        self.prev = url.deletingLastPathComponent()
        self.parent = parent
    }
}

2. 添加工具方法:获取节点到根的完整路径

给DirectoryItem扩展一个方法,用来获取从当前节点到根节点的所有节点(顺序是根→目标节点):

extension DirectoryItem {
    func getPathToRoot() -> [DirectoryItem] {
        var path: [DirectoryItem] = []
        var currentItem: DirectoryItem? = self
        // 从当前节点往上遍历父节点,直到根
        while let item = currentItem {
            path.insert(item, at: 0) // 插入到数组开头,保证路径顺序是根到目标
            currentItem = item.parent
        }
        return path
    }
}

3. 实现自动展开路径的方法

在ViewController中添加一个方法,用来展开指定节点的完整路径,并选中目标节点:

private func expandPath(to targetItem: DirectoryItem) {
    let pathItems = targetItem.getPathToRoot()
    // 逐个展开路径上的所有节点
    for item in pathItems {
        outlineView.expandItem(item)
    }
    // 选中目标节点,模拟用户上次访问的位置
    if let row = outlineView.row(forItem: targetItem) {
        outlineView.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
    }
}

4. 添加查找节点的方法

需要根据保存的URL找到对应的DirectoryItem节点,添加递归查找方法:

private func findItem(with url: URL, in parentItem: DirectoryItem) -> DirectoryItem? {
    // 先判断当前父节点是否就是目标节点
    if parentItem.url == url {
        return parentItem
    }
    // 触发懒加载的childItems,确保子节点已初始化
    _ = parentItem.childItems
    // 递归查找子节点
    for child in parentItem.childItems {
        if let foundItem = findItem(with: url, in: child) {
            return foundItem
        }
    }
    return nil
}

5. 修改viewDidLoad,恢复上次访问路径

最后,在viewDidLoad中加载完根目录后,查找上次保存的目录并自动展开:

override func viewDidLoad() {
    super.viewDidLoad()
    let defaults = UserDefaults.standard
    let lastDirectory = defaults.url(forKey: "LastDirectory")
    
    // 初始化根节点为用户home目录(你可以根据需求修改为其他根目录)
    rootItem = DirectoryItem(url: FileManager.default.homeDirectoryForCurrentUser)
    
    outlineView.dataSource = self
    outlineView.delegate = self
    tableView.delegate = self
    tableView.dataSource = self
    tableView.doubleAction = #selector(doubleClickOnResultRow)
    self.progressIndicator.isHidden = true
    self.tableView.reloadData()
    
    // 如果有保存的上次访问目录,查找并展开路径
    if let targetUrl = lastDirectory {
        if let targetItem = findItem(with: targetUrl, in: rootItem) {
            expandPath(to: targetItem)
        }
    }
}

对应你的两个问题的说明

  • 问题1:从子节点展开至最顶层父节点:通过getPathToRoot()方法可以获取子节点到根的所有节点,再调用outlineView.expandItem()逐个展开即可,上面的expandPath方法已经实现了这个逻辑。
  • 问题2:从根节点展开所有层级至目标节点:同样通过getPathToRoot()拿到根到目标的路径,逐个展开,同时选中目标节点,这正是恢复上次访问路径的核心逻辑。

这样修改后,用户关闭再打开应用时,就会自动展开到上次访问的目录节点,无需手动点击展开啦~

内容的提问来源于stack exchange,提问作者Renan Aguiar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:04:45