使用UIDocumentBrowserViewController创建文档

documentBrowser(_:didRequestDocumentCreationWithHandler:)说:“创建一个新文档并将其保存到临时位置。如果使用UIDocument子类创建文档,则必须在调用importHandler块之前关闭它。”

所以我通过获取用户临时目录( FileManager.default.temporaryDirectory )的URL并附加名称和扩展名(获取类似“file:/// private / var / mobile / Containers / Data / Application”的路径来创建文件URL /C1DE454D-EA1E-4166-B137-5B43185169D8/tmp/Untitled.uti“)。 但是当我调用save(to:for:completionHandler:)传递此URL时,永远不会回调完成处理程序。 我也尝试使用url(for:in:appropriateFor:create:)来传递用户临时目录中的子目录 – 仍然没有调用完成处理程序。

我理解文档浏览器视图控制器由一个单独的进程管理,该进程有自己的读/写权限。 除此之外,我很难理解问题所在。 临时保存新文档的位置,以便文档浏览器进程可以移动它们?

更新:截至目前的测试版,我现在看到域NSFileProviderInternalErrorDomain并且代码1被记录:“不允许读者访问该URL。” 至少那是对发生了什么的确认……

因此,首先,如果您使用的是自定义UTI,则必须正确设置。 我看起来像这样……

 CFBundleDocumentTypes   CFBundleTypeIconFiles  icon-file-name // Can be excluded, but keep the array  CFBundleTypeName Your Document Name CFBundleTypeRole Editor LSHandlerRank Owner LSItemContentTypes  com.custom-uti    

 UTExportedTypeDeclarations   UTTypeConformsTo  public.data // My doc is saved as Data, not a file wrapper  UTTypeDescription Your Document Name UTTypeIdentifier com.custom-uti UTTypeTagSpecification  public.filename-extension  doc-extension     

 UISupportsDocumentBrowser  

我将UIDocument子类UIDocument MyDocument并添加以下方法来创建新的临时文档…

 static func create(completion: @escaping Result -> Void) throws { let targetURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("Untitled").appendingPathExtension("doc-extension") coordinationQueue.async { let document = MyDocument(fileURL: targetURL) var error: NSError? = nil NSFileCoordinator(filePresenter: nil).coordinate(writingItemAt: targetURL, error: &error) { url in document.save(to: url, for: .forCreating) { success in DispatchQueue.main.async { if success { completion(.success(document)) } else { completion(.failure(MyDocumentError.unableToSaveDocument)) } } } } if let error = error { DispatchQueue.main.async { completion(.failure(error)) } } } } 

然后按如下方式初始化并显示DBVC:

 @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { lazy var documentBrowser: UIDocumentBrowserViewController = { let utiDecs = Bundle.main.object(forInfoDictionaryKey: kUTExportedTypeDeclarationsKey as String) as! [[String: Any]] let uti = utiDecs.first?[kUTTypeIdentifierKey as String] as! String let dbvc = UIDocumentBrowserViewController(forOpeningFilesWithContentTypes:[uti]) dbvc.delegate = self return dbvc }() func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) window?.rootViewController = documentBrowser window?.makeKeyAndVisible() return true } } 

我的委托方法如下:

 func documentBrowser(_ controller: UIDocumentBrowserViewController, didRequestDocumentCreationWithHandler importHandler: @escaping (URL?, UIDocumentBrowserViewController.ImportMode) -> Swift.Void) { do { try MyDocument.create() { result in switch result { case let .success(document): // .move as I'm moving a temp file, if you're using a template // this will be .copy importHandler(document.fileURL, .move) case let .failure(error): // Show error importHandler(nil, .none) } } } catch { // Show error importHandler(nil, .none) } } func documentBrowser(_ controller: UIDocumentBrowserViewController, didImportDocumentAt sourceURL: URL, toDestinationURL destinationURL: URL) { let document = MyDocument(fileURL: destinationURL) document.open { success in if success { // Modally present DocumentViewContoller for document } else { // Show error } } } 

这就是它。 让我知道你是怎么办的!

我有同样的问题,但后来我意识到推荐的方法是简单地从Bundle中复制包/文件夹,如下所示:

 func documentBrowser(_ controller: UIDocumentBrowserViewController, didRequestDocumentCreationWithHandler importHandler: @escaping (URL?, UIDocumentBrowserViewController.ImportMode) -> Void) { if let url = Bundle.main.url(forResource: "Your Already Created Package", withExtension: "your-package-extension") { importHandler(url, .copy) } else { importHandler(nil, .none) } } 

为了澄清,这个包只是你创建的文件夹,并且已经填入Xcode。

如果您考虑这种方法,这种方法是有道理的,原因如下:

  1. Apple文件系统(AFS)。 向AFS的转变意味着复制(几乎)是免费的。

  2. 权限。 始终允许从Bundle复制,并且用户指定要复制到的位置。

  3. 新文档浏览器范例。 由于我们正在使用新的UIDocumentBrowserViewController范例(由于iOS11和新的Files应用程序),它甚至处理命名(参见Apple的Pages)以及移动和排列文件。 我们不必担心在哪个线程上运行。

所以。 更简单,更容易,也可能更好。 我想不出手动创建所有文件(或使用临时文件夹等)的原因。

在设备上测试,而不是在模拟器中测试。 在我切换到设备测试的那一刻,一切都刚刚开始正常工作。 (注意:可能是模拟器故障仅发生在像我这样的Sierra用户身上。)