如何在 iOS 14+ 及更低版本上向应用程序添加文件选择器

And*_*hko 6 ios swift

我是 iOS 开发的新手,所以我在这里展示和询问的一些事情可能很愚蠢,请不要生气:) 因此,我需要在我的应用程序中添加从本地存储中选取文件的支持。此功能将用于选择文件 -> 编码为 Base64,然后发送到远程服务器。现在,我在将此功能添加到我的应用程序时遇到一些问题。我找到了本教程并执行了此处提到的所有操作:

  1. 添加了导入 -import MobileCoreServices

  2. 添加实施 -UIDocumentPickerDelegate

  3. 添加了此代码范围以显示选择器:

    let documentPicker = UIDocumentPickerViewController(documentTypes: [String(kUTTypeText),String(kUTTypeContent),String(kUTTypeItem),String(kUTTypeData)], in: .import)
    documentPicker.delegate = self
    self.present(documentPicker, animated: true)
    
    Run Code Online (Sandbox Code Playgroud)
  4. 还添加了所选文件的处理程序:

    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
    print(urls)
    }
    
    Run Code Online (Sandbox Code Playgroud)

一般来说,文件选择器出现在模拟器屏幕上,但我在 XCode 中看到警告:

'init(documentTypes:in:)' was deprecated in iOS 14.0
Run Code Online (Sandbox Code Playgroud)

我访问了官方指南,在这里还发现了有关弃用某些方法的类似信息。那么,我如何才能解决我的文件选择问题,以与最新的iOS版本完全兼容。另一个问题 - 我如何对选定的文件进行编码?现在我可以选择文件并打印其位置,但我需要获取其数据,如名称、编码内容等。也许有人面临类似的问题并且知道解决方案?我需要将其添加到普通视图控制器中,因此当我尝试添加此实现时:

UIDocumentPickerViewController
Run Code Online (Sandbox Code Playgroud)

我看到这样的错误信息:

Multiple inheritance from classes 'UIViewController' and 'UIDocumentPickerViewController'
Run Code Online (Sandbox Code Playgroud)

我将非常高兴获得任何信息:教程或建议:)

Mat*_*lak 1

一旦您有了文件 URL,您就可以使用该 URL 来检索它包含的数据。获得数据后,可以将其转换为 Base64 并将其发送到服务器。您没有提供有关如何将其发送到服务器的信息,但其余部分可能如下所示:

func sendFileWithURL(_ url: URL, completion: @escaping ((_ error: Error?) -> Void)) {
    func finish(_ error: Error?) {
        DispatchQueue.main.async {
            completion(error)
        }
    }
    
    DispatchQueue(label: "DownloadingFileData." + UUID().uuidString).async {
        do {
            let data: Data = try Data(contentsOf: url)
            let base64String = data.base64EncodedString()
            // TODO: send string to server and call the completion
            finish(nil)
        } catch {
            finish(error)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

你会用它作为

func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
    urls.forEach { sendFileWithURL($0) { <#Your code here#> } }
}
Run Code Online (Sandbox Code Playgroud)

分解它:

要获取文件数据,您可以使用Data(contentsOf: url). 此方法甚至适用于远程文件,因此您可以使用您可以访问的互联网上任何位置的图像链接的 URL。重要的是要知道此方法将暂停您的线程,这通常不是您想要的。

为了避免破坏当前线程,我们使用创建一个新队列DispatchQueue(label: "DownloadingFileData." + UUID().uuidString)。队列的名称不是很重要,但在调试时很有用。

收到数据后,我们将其转换为 Base64 字符串data.base64EncodedString(),然后可以将该数据发送到服务器。您只需填写该TODO:部分即可。

检索文件数据可能会出现一些错误。也许访问限制或文件不再存在或没有互联网连接...这是通过抛出来处理的。如果语句因try任何原因失败,则该catch部分将执行并且您会收到错误。

由于所有这些都是在后台线程上完成的,因此返回主线程通常是有意义的。这就是该finish函数的作用。如果您不需要,只需将其删除即可:

func sendFileWithURL(_ url: URL, completion: @escaping ((_ error: Error?) -> Void)) {
    DispatchQueue(label: "DownloadingFileData." + UUID().uuidString).async {
        do {
            let data: Data = try Data(contentsOf: url)
            let base64String = data.base64EncodedString()
            // TODO: send string to server and call the completion
            completion(nil)
        } catch {
            completion(error)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这种方法还需要考虑其他事项。例如,您可以看到如果用户选择多个文件,那么每个文件都会打开自己的队列并启动该过程。这意味着,如果用户选择多个文件,则可能在某个时刻许多或全部文件将被加载到内存中。这可能会占用太多内存并使您的应用程序崩溃。您可以决定这种方法是否适合您或者您希望序列化该过程。使用队列的序列化应该非常简单。您所需要的只是拥有一个:

private lazy var fileProcessingQueue: DispatchQueue = DispatchQueue(label: "DownloadingFileData.main")

func sendFileWithURL(_ url: URL, completion: @escaping ((_ error: Error?) -> Void)) {
    func finish(_ error: Error?) {
        DispatchQueue.main.async {
            completion(error)
        }
    }
    
    fileProcessingQueue.async {
        do {
            let data: Data = try Data(contentsOf: url)
            let base64String = data.base64EncodedString()
            // TODO: send string to server and call the completion
            finish(nil)
        } catch {
            finish(error)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,一项操作将在另一项操作开始之前完成。但这可能仅适用于获取文件数据并转换为 Base64 字符串。如果上传是在另一个线程上完成的(通常是这样),那么您可能仍然有多个正在进行的请求,其中可能包含上传所需的所有数据。