UIActivityViewController "Сохранить в файлы" сохраняет несколько файлов, когда требуется только 1 файл.

Мое приложение может предоставлять различные типы UIActivityItem (текст, данные, форматированный текст, средство визуализации страницы печати и т. Д.) Для совместного использования для различных целей, включая печать, копирование / вставку и сохранение в виде файла. Для целей копирования / вставки он должен включать простой текст, атрибутивную строку, данные (данные JSON) и строку JSON).

Однако, поскольку предоставляется несколько типов данных, параметр "Сохранить в файлы" UIActivityViewController приводит к сохранению нескольких файлов - по одному для каждого из типов элементов, которые можно сохранить в виде файла.

Если я уменьшу его до одного UIActivityItem, тогда функциональность копирования / вставки будет значительно сокращена, так что он не будет работать со всеми различными типами монтажного стола, которые должен (например, пользовательский формат данных JSON моего приложения И с простым текстом И строкой с атрибутами).

Поэтому я пытаюсь использовать подкласс 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???
                //
            }
        }
    }

}

Когда я запускаю это и выбираю "Сохранить в файлы", я получаю следующий результат (по одной строке от каждого провайдера):

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

... и файл создается для каждого из них, если я просто передаю элемент для этого типа данных.

1 ответ

Решение

Что ж, я обнаружил, что решение было двояким...

Прямой ответ на вопрос (но не идеальное поведение):

В каждом из switch случаи (и if в пределах default case) мне нужно было проверить, что activityType соответствует placeholderItem. Если это не подходящее совпадение, то верните (пустой) placeholderItem как есть (за исключением того, что в случае "Сохранить в файлы", даже пустой заполнитель средства визуализации страницы печати приводил к записи файла! Поэтому вместо возврата заполнителя там верните пустой массив).

Это сработало и привело к записи одного файла в выбранную пользователем папку, которая отвечает на исходный вопрос. Пользователь даже получает возможность указать имя для файла. Но имя по умолчанию совсем не подходит - только тип данных (например, "текст" или "данные", в зависимости от того, что было сохранено в файл).

Решение, обеспечивающее большую гибкость: создайте индивидуальныйUIActivity который запишет файл в место, выбранное пользователем с помощью UIDocumentPickerViewController. Например, действие может иметь название типа "Экспорт в").

Это оказалось намного более гибким, и я позволил мне использовать имя файла по умолчанию (и расширение!), Которое имело гораздо больше смысла на основе передаваемых данных. У меня также есть потенциал для внесения дополнительных улучшений в поведение позже (например, я мог бы использовать предупреждение, чтобы заставить пользователя выбирать между парой различных форматов файлов (для использования в разных целях).

Это не заменяет действие "Сохранить в файлы", поэтому я использую их оба, и мне все еще нужно исправить поведение моего действия "Сохранить в файлы", как описано выше.

Это оставляет мне действия "Экспорт" и "Сохранить", которые работают одинаково, но одно более гибкое и интуитивно понятное, чем другое, и я могу заставить их использовать разные форматы файлов по умолчанию.

Мой новый код (для default часть внешнего switch) ниже...

        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
        }
Другие вопросы по тегам