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" 动作,它们的工作方式相似,但一个比另一个更灵活和直观,我可以让它们使用不同的文件格式默认。
我的新代码(外部 switch
的 default
部分)在下面...
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
}
我的应用程序能够提供多种 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" 动作,它们的工作方式相似,但一个比另一个更灵活和直观,我可以让它们使用不同的文件格式默认。
我的新代码(外部 switch
的 default
部分)在下面...
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
}