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

iOS中UIDocumentPickerViewController无法记忆上次访问文件夹问题排查

解决UIDocumentPicker在iOS16+iCloud场景下无法记住上次访问文件夹的问题

问题背景

你的代码在未激活iCloud的设备上能正常记住Files应用上次访问的文件夹,但在激活iCloud的iOS16设备上每次都会打开初始目录,单独打开Files应用则无此问题。这是因为iOS14+的UIDocumentPickerViewController在iCloud场景下不会自动保留上次访问位置,需要手动指定初始目录并持久化用户的选择。

解决方案

通过以下步骤修复:

  1. 持久化上次访问的目录
    在你的Presenter类中添加一个属性,用于保存并读取用户上次选择的文件夹URL:
private var lastAccessedDirectoryURL: URL? {
    get {
        guard let data = UserDefaults.standard.data(forKey: "LastAccessedDirectoryURL"),
              let url = try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSURL.self, from: data) as URL? else {
            return nil
        }
        // 验证目录是否仍可访问
        return FileManager.default.fileExists(atPath: url.path) ? url : nil
    }
    set {
        guard let url = newValue else {
            UserDefaults.standard.removeObject(forKey: "LastAccessedDirectoryURL")
            return
        }
        guard let data = try? NSKeyedArchiver.archivedData(withRootObject: url, requiringSecureCoding: false) else {
            return
        }
        UserDefaults.standard.set(data, forKey: "LastAccessedDirectoryURL")
    }
}
  1. 修改文件选择器初始化代码
    在创建UIDocumentPickerViewController时,为iOS14+设备设置initialDirectoryURL为上次访问的目录:
func goToFilesApp() {
    var documentPicker: UIDocumentPickerViewController
    if #available(iOS 14, *) {
        let types: [UTType] = [UTType.text,
                               UTType.vCard,
                               UTType.zip,
                               UTType.gzip]
        documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: types)
        // 设置初始目录为上次访问的文件夹
        if let lastDir = lastAccessedDirectoryURL {
            documentPicker.initialDirectoryURL = lastDir
        }
    } else {
        let types = [String(kUTTypeText),
                     String(kUTTypeVCard),
                     "com.pkware.zip-archive",
                     "org.gnu.gnu-zip-archive",
                     "org.gnu.gnu-zip-tar-archive"]
        documentPicker = UIDocumentPickerViewController(documentTypes: types, in: .import)
    }
    documentPicker.delegate = self
    documentPicker.modalPresentationStyle = .formSheet
    
    // 修正原代码的视图控制器转换错误:UIView不能转为UIViewController
    // 确保你持有正确的UIViewController实例来present
    if let viewController = self as? UIViewController {
        viewController.present(documentPicker, animated: true, completion: nil)
    }
    // 如果是Presenter模式,替换为你持有的viewController引用,比如:
    // self.viewController?.present(documentPicker, animated: true)
}
  1. 更新代理方法保存目录并获取权限
    在文件选择完成后,提取文件所在目录并保存,同时获取iCloud文件的持久化访问权限:
extension AnalyzeFilePresenter : UIDocumentPickerDelegate {
    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        guard let firstUrl = urls.first else { return }
        
        // 获取文件所在的父目录
        let directoryURL = firstUrl.deletingLastPathComponent()
        
        // 请求iCloud文件的持久化访问权限
        firstUrl.startAccessingSecurityScopedResource()
        defer {
            firstUrl.stopAccessingSecurityScopedResource()
        }
        
        // 保存目录到本地
        lastAccessedDirectoryURL = directoryURL
        
        manageFilePicked(url: firstUrl)
    }
    
    // 实现取消代理方法,避免内存泄漏
    func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
        controller.dismiss(animated: true, completion: nil)
    }
}

关键注意事项

  • 原代码中self.view as? UIViewController是错误的,UIView无法转换为UIViewController,需确保使用正确的视图控制器实例来present选择器。
  • 对于iOS14+设备,必须显式设置initialDirectoryURL,系统在iCloud场景下不会自动保留上次访问位置。
  • 保存URL到UserDefaults时必须使用归档,因为URL不能直接存储。
  • 访问iCloud文件时必须调用startAccessingSecurityScopedResource()和stopAccessingSecurityScopedResource(),否则保存的URL后续可能无法访问。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 03:45:35