UIActivityViewController "Save to Files" 在只需要 1 个文件时保存多个文件

UIActivityViewController "Save to Files" saves multiple files when only 1 file is required

我的应用程序能够提供多种 UIActivityItem 类型(文本、数据、富文本、打印页面渲染器等)以供出于各种目的共享,包括打印、copy/paste 和另存为一份文件。对于copy/paste,需要包含纯文本、属性字符串、数据(JSON数据)和JSON字符串)。

但是,由于提供了多种数据类型,UIActivityViewController 的 "Save to Files" 选项会导致保存多个文件 - 每个项目类型都可以保存为一个文件。

如果我将它减少到只有一个 UIActivityItem,那么 copy/paste 功能会严重减少,这样它就无法使用它应该使用的所有各种不同的粘贴板类型(例如,我的应用程序的自定义 JSON 数据格式 AND 纯文本 AND 属性字符串)。

所以我试图使用 UIActivityItemProvider 子类来解决这个问题,但我仍然无法弄清楚如何只保存一个文件,而不是多个文件(每个项目类型一个)。这甚至可能吗?

我的 UIActivityItemProvider 子类的相关部分如下。

(请注意,我为此使用了单个子类的多个实例,但使用不同的子类也会出现同样的问题。)

class RatsActivityItemProvider: UIActivityItemProvider {

    var rats: [Rat]

    init(placeholderItem: Any, rats: [Rat]) {
        RatsActivityItemProvider.selectedOption = nil
        self.rats = rats
        super.init(placeholderItem: placeholderItem)
    }

    class func allProviders(forRats rats: [Rat]) -> [RatsActivityItemProvider] {
        var providers: [RatsActivityItemProvider] = []
        providers.append(RatsActivityItemProvider(placeholderItem: NSAttributedString(), rats: rats))
        providers.append(RatsActivityItemProvider(placeholderItem: RatPrintPageRenderer(), rats: rats))
        providers.append(RatsActivityItemProvider(placeholderItem: [:] as [String:Any], rats: rats))
        providers.append(RatsActivityItemProvider(placeholderItem: Data(), rats: rats))
        return providers
    }

    override var item: Any {
        print("\(activityType!.rawValue as Any) - \(type(of: placeholderItem!))")
        switch activityType {
        case UIActivity.ActivityType.print:
            return RatPrintPageRenderer(rats)
        case UIActivity.ActivityType.copyToPasteboard:
            var pasteboardDict: [String:Any] = attrString.pasteables()
            //  (Add custom types to dictionary here)
            return pasteboardDict
        default:
            //  "Save To Files" activity is not available in UIActivity.ActivityType so check the raw value instead
            if activityType?.rawValue.contains("com.apple.CloudDocsUI.AddToiCloudDrive") ?? false {
                //
                //  HOW TO HAVE ONLY ONE OF THE PROVIDERS RETURN A VALUE HERE???
                //
            }
        }
    }

}

当我 运行 并选择 "Save to Files" 时,我得到以下输出(每个提供者一行):

com.apple.CloudDocsUI.AddToiCloudDrive - NSConcreteAttributedString
com.apple.CloudDocsUI.AddToiCloudDrive - RecipePrintPageRenderer
com.apple.CloudDocsUI.AddToiCloudDrive - __EmptyDictionarySingleton
com.apple.CloudDocsUI.AddToiCloudDrive - _NSZeroData

...如果我简单地传回该数据类型的项目,就会为其中的每一个创建一个文件。

嗯,我发现解决方案是 two-fold...

直接回答问题(但不是理想的行为):

在每个 switch 案例中(以及 default 案例中的 if),我需要检查 activityType 是否与 placeholderItem.如果不是合适的匹配项,则 return(空的)placeholderItem as-is(除了在 "Save to Files" 的情况下,即使是空的打印页面渲染器占位符也会导致写入文件!所以不是return那里的占位符,return一个空数组)。

这行得通,并导致将单个文件写入用户选择的位置,从而回答了原始问题。用户甚至有机会为文件提供名称。但是默认名称一点也不好——只是数据类型(例如,"text" 或 "data",取决于保存到文件中的内容)。

提供更多灵活性的解决方案: 创建自定义 UIActivity,将文件写入用户使用 UIDocumentPickerViewController 选择的位置。例如,activity 的标题可以是 "Export to ").

这被证明更加灵活,让我使用默认文件名(和扩展名!),根据传入的数据,这更有意义。它也有可能让我稍后对行为进行进一步的改进(例如,我可以使用警报让用户在几种不同的文件格式之间进行选择(用于不同的目的)。

这不会取代 "Save to Files" 操作,所以我最终得到了它们,并且仍然需要如上所述修复我的 "Save to Files" 操作的行为。

它给我留下了一个 "Export" 和一个 "Save" 动作,它们的工作方式相似,但一个比另一个更灵活和直观,我可以让它们使用不同的文件格式默认。

我的新代码(外部 switchdefault 部分)在下面...

        default:
            if activityType?.rawValue == "com.stuff.thing.activity.export" {
                if placeholderItem is [Rat] {
                    return rats
                } else {
                   return placeholderItem!
               }
            } else if activityType?.rawValue == "com.apple.CloudDocsUI.AddToiCloudDrive" {
                if placeholderItem is Data {
                    return attrString
                } else {
                    //  Don't return the placeholder item here!  Some (eg print page renderer) can result in a file being written!
                    return [] as [Any]
                }
            }

            return attrString
        }