8

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

所以我通过获取用户临时目录 ( FileManager.default.temporaryDirectory) 的 URL 并附加名称和扩展名来创建一个文件 URL 4166-B137-5B43185169D8/tmp/Untitled.uti")。但是当我调用save(to:for:completionHandler:)传递这个 URL 时,完成处理程序永远不会被回调。我还尝试使用url(for:in:appropriateFor:create:)传递用户临时目录中的子目录——完成处理程序仍然没有被调用。

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

更新:在当前的测试版中,我现在看到域NSFileProviderInternalErrorDomain和代码1被记录的错误:“不允许读者访问 URL。” 至少这是对正在发生的事情的确认……</p>

4

3 回答 3

13

因此,首先,如果您使用的是自定义 UTI,则必须正确设置它。我的长这样……</p>

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

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

<key>UISupportsDocumentBrowser</key>
<true/>

我将子类UIDocument化为MyDocument并添加以下方法来创建一个新的临时文档……</p>

static func create(completion: @escaping Result<MyDocument> -> 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
        }
    }
}

差不多就是这样。让我知道你是怎么办的!

于 2017-08-03T17:17:33.130 回答
3

我遇到了同样的问题,但后来我意识到推荐的方法是简单地从 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. 苹果文件系统 (AFS)。转向 AFS 意味着复制(几乎)是免费的。

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

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

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

于 2017-12-12T23:59:37.300 回答
1

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

于 2017-11-04T20:19:36.697 回答