iOS:在本机文件应用程序中打开保存的图像

iOS: Open saved image inside the native Files app

我的应用程序允许将图像保存到本机文件应用程序。我这样做:

func save(imageData: Data,
          toFolder folderName: String,
          withFileName fileName: String) {
    DispatchQueue.global().async {
        let manager = FileManager.default
        let documentFolder = manager.urls(for: .documentDirectory, in: .userDomainMask).last
        let folder = documentFolder?.appendingPathComponent(folderName)
        let file = folder?.appendingPathComponent(fileName)

        do {
            try manager.createDirectory(at: folder!, withIntermediateDirectories: true, attributes: [:])
            if let file = file {
                try imageData.write(to: file)
            }
        } catch {
            print(error.localizedDescription)
        }
    }
}

但是,当我在“文件”应用程序中点击图片时,系统会导航到我的应用程序,而不是在“文件”应用程序中打开图片。

如何更改?

发生在 Snapseed 应用程序(Google 的应用程序)上的相同不良行为示例 https://media.giphy.com/media/Odnyy9J52HJjhmr0Fl/giphy.gif

经过一些研究,我认为这不是不需要的行为,而是 Snapseed 订阅的行为,因为这种行为不会自动发生。

但是,有一些错误(我认为)不会让您轻松取消订阅此行为。

我们需要注意的地方是使用 LSSupportsOpeningDocumentsInPlaceCFBundleDocumentTypes,更多关于 here and here:

我在这里创建了一个小示例,当用户使用您的代码点击按钮时,将图像从应用程序主包保存到文档目录:

与您的代码完全没有区别

@objc
private func saveImage()
{
    if let imageData
        = UIImage(named: "dog")?.jpegData(compressionQuality: 1.0)
    {
        save(imageData: imageData,
             toFolder: "image",
             withFileName: "dog.jpeg")
    }
}

func save(imageData: Data,
          toFolder folderName: String,
          withFileName fileName: String)
{
    DispatchQueue.global().async
    {
        let manager = FileManager.default
        let documentFolder = manager.urls(for: .documentDirectory,
                                          in: .userDomainMask).last
        let folder = documentFolder?.appendingPathComponent(folderName)
        let file = folder?.appendingPathComponent(fileName)

        do {
            try manager.createDirectory(at: folder!,
                                        withIntermediateDirectories: true,
                                        attributes: [:])
            if let file = file
            {
                try imageData.write(to: file)
                print("file \(fileName) saved")
            }
        }
        catch
        {
            print(error.localizedDescription)
        }
    }
}

如果您将以下内容添加到 info.plist

<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>

这支持在文件应用程序中公开您的文档目录,此后一切正常:

但是,如果您希望您的应用属于打开特定文件格式的组,您可以修改您的 info.plist 以添加 CFBundleDocumentTypes 以建议您的应用能够打开特定文件格式文件,例如我们案例中的图像。

为此,我们将以下内容添加到 info.plist

<key>CFBundleDocumentTypes</key>
    <array>
        <dict>
            <key>LSItemContentTypes</key>
            <array>
                <string>public.image</string>
            </array>
            <key>CFBundleTypeName</key>
            <string>image</string>
            <key>LSHandlerRank</key>
            <string>Default</string>
        </dict>
    </array>

这将允许您的应用程序在有人想要共享或打开图像时列出,通过添加它,然后我们得到您的 gif 中显示的行为,即打开应用程序而不是在文件应用程序中预览

不幸的是,似乎一旦将此行为分配给您的应用程序,即使您从 info.plist 中删除上述键,此行为仍然存在。

我可以恢复原始行为的唯一方法是使用新的包标识符,并且只在 info.plist 中使用这两个键,以便将您的文档目录公开给文件应用程序

<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>

我认为这是某种错误,因为应该有一种简单的方法可以取消订阅此行为。

希望这对您有所帮助,尽管它不能完全解决您的问题