在Swift(iOS)中实现文档选择器

75

我希望在我的iOS应用程序中实现选择任何类型的文件(.pdf, .docs, .xlsx, .jpeg, .txt, .rtf等)功能。当点击上传按钮时,我希望我的应用程序能够打开一个目录并选择文件(DocumentsPicker)。

@IBAction pickDocument(sender: UIButton) {
    //Open Document Picker
}

有任何方法在Swift中实现这个吗?


1
iOS应用程序受到沙盒保护,因此您无法打开应用沙盒之外的任何目录。而且,您的问题太宽泛了,没有显示出您尝试自己解决此功能的任何努力。 - rckoenes
@Nilesh Pol:如果我需要从这里选择文件并上传到服务器,我该怎么办?我必须先下载它然后通过多部分发送吗?还是有其他方法可以做到这一点?请给予建议。 - Ishika
@Ishika 文档选择器已为您下载好,您无需自行操作。 - Surjeet Rajput
10个回答

117

iOS 14更新:您不需要任何权限。只需创建具有适当类型的UIDocumentPickerViewController,实现代理即可完成。

更多信息请参阅此答案。 代码如下:


import UIKit
import MobileCoreServices
import UniformTypeIdentifiers

func selectFiles() {
    let types = UTType.types(tag: "json", 
                             tagClass: UTTagClass.filenameExtension, 
                             conformingTo: nil)
    let documentPickerController = UIDocumentPickerViewController(
            forOpeningContentTypes: types)
    documentPickerController.delegate = self
    self.present(documentPickerController, animated: true, completion: nil)
}


根据您的项目能力,启用和键共享

输入图像描述

在您的类中导入MobileCoreServices,然后在UIViewController内扩展以下三个类:

UIDocumentMenuDelegate,UIDocumentPickerDelegate,UINavigationControllerDelegate

请实现以下函数:

public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
    guard let myURL = urls.first else {
        return
    }
    print("import result : \(myURL)")
}
      

public func documentMenu(_ documentMenu:UIDocumentMenuViewController, didPickDocumentPicker documentPicker: UIDocumentPickerViewController) {
    documentPicker.delegate = self
    present(documentPicker, animated: true, completion: nil)
}


func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
    print("view was cancelled")
    dismiss(animated: true, completion: nil)
}
如何称呼这一切?在您的点击函数中添加以下代码段:
func clickFunction(){

let importMenu = UIDocumentMenuViewController(documentTypes: [String(kUTTypePDF)], in: .import)
    importMenu.delegate = self
    importMenu.modalPresentationStyle = .formSheet       
    self.present(importMenu, animated: true, completion: nil)
}

点击按钮后,将弹出以下菜单:

MenuPicker

以Dropbox为例,点击任何项目后,您将被重定向回您的应用程序,并且URL将记录在您的终端中。

enter image description here

根据您的需要操纵documentTypes。在我的应用程序中,仅允许用户使用PDF,所以请随意自行适应。

kUTTypePDF kUTTypePNG kUTTypeJPEG ...

如果您想自定义您自己的菜单栏,请添加以下代码并在处理程序内自定义您自己的功能。

importMenu.addOption(withTitle: "Create New Document", image: nil, order: .first, handler: { print("New Doc Requested") })

在此输入图片描述

享受它。


1
无法仅设置为 PDF,只显示“public.pdf”错误。错误信息为“传递的类型 public.pdf 不符合 public.content 或 public.item 的任何一个。如果您正在导出新类型,请确保它符合适当的父类型。” - Subhojit Mandal
1
为什么我得到“无可用位置”? - Rohit Dhawan
1
如何在选择所需位置后查看文件? - Deepakraj Murugesan
3
如果我需要从这里选择文件并将其上传到服务器,我该怎么做?我是需要先下载文件然后再通过multipart的方式发送吗?还是有其他方法可以实现? - Ishika
1
不要忘记“import MobileCoreServices”,否则String(kUTTypePDF)会出错。 - Muhammad Nayab
显示剩余9条评论

40

您可以使用UIDocumentPickerViewController从文件应用程序或iCloud Drive获取文件。

  1. 激活iCloud功能下的键值存储iCloud文档

enter image description here

  1. Import the following framework on the view controller you want to open the document picker:

     import MobileCoreServices
    
     // For iOS 14+
     import UniformTypeIdentifiers
    
  2. Implement the following method from UIDocumentPickerDelegate:

     func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
          // you get from the urls parameter the urls from the files selected
     }
    
  3. Create an UIDocumentPickerViewController to display the File picker or iCloud Drive:

    // Use this code if your are developing prior iOS 14
    let types: [String] = [kUTTypePDF as String]
    let documentPicker = UIDocumentPickerViewController(documentTypes: types, in: .import)
    documentPicker.delegate = self
    documentPicker.modalPresentationStyle = .formSheet
    self.present(documentPicker, animated: true, completion: nil)
    
    // For iOS 14+
    let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [UTType.item], asCopy: false)
    documentPicker.delegate = self
    documentPicker.modalPresentationStyle = .formSheet
    
    self.present(documentPicker, animated: true, completion: nil)
    
如果您希望用户可以将更多类型的文件导入到您的应用程序中,您需要向typesNSArray中添加更多UTType。要查看所有可用类型,您可以检查UTType文档
当文档选择器在iOS 11上打开并且您尝试从Google Drive中选择文件时,由于错误,该文件可能被禁用:http://www.openradar.me/24187873

12
为什么我们需要iCloud的键值存储? - jomafer
@jomafer 在这个答案中不需要它。 - Hatim
2
那么为什么步骤1中提到了它呢?“激活键值存储和[...]” - Ricardo Yubal

24

UIDocumentMenuViewController 自 iOS 11 起已被弃用。我还发现当从模态视图控制器中呈现时,它很容易出现故障。 以下是直接使用选择器的方法:

import MobileCoreServices

private func attachDocument() {
    let types = [kUTTypePDF, kUTTypeText, kUTTypeRTF, kUTTypeSpreadsheet]
    let importMenu = UIDocumentPickerViewController(documentTypes: types as [String], in: .import)

    if #available(iOS 11.0, *) {
        importMenu.allowsMultipleSelection = true
    }

    importMenu.delegate = self
    importMenu.modalPresentationStyle = .formSheet

    present(importMenu, animated: true)
}

extension AViewController: UIDocumentPickerDelegate, UINavigationControllerDelegate {
    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        viewModel.attachDocuments(at: urls)
    }

     func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
        controller.dismiss(animated: true, completion: nil)
    }
}

像往常一样,不要忘记添加iCloud支持:iCloud支持


你是不是指的是iCloud文档而不是键值存储? - Hatim
@Hatim 我没有启用iCloud文档,只启用了键值存储。这已经足够了,不需要任何额外的设置。 - SoftDesigner

9

UIDocumentPickerViewController(documentTypes: [String], in: UIDocumentPickerMode)在iOS 14.0中已被废弃

现在使用UIDocumentPickerViewController(forOpeningContentTypes: [UTType])

ContentTypes是一个包含以下任何或组合的数组:

UTType.image, UTType.text, UTType.plainText, UTType.utf8PlainText,    UTType.utf16ExternalPlainText, UTType.utf16PlainText,    UTType.delimitedText, UTType.commaSeparatedText,    UTType.tabSeparatedText, UTType.utf8TabSeparatedText, UTType.rtf,    UTType.pdf, UTType.webArchive, UTType.image, UTType.jpeg,    UTType.tiff, UTType.gif, UTType.png, UTType.bmp, UTType.ico,    UTType.rawImage, UTType.svg, UTType.livePhoto, UTType.movie,    UTType.video, UTType.audio, UTType.quickTimeMovie, UTType.mpeg,    UTType.mpeg2Video, UTType.mpeg2TransportStream, UTType.mp3,    UTType.mpeg4Movie, UTType.mpeg4Audio, UTType.avi, UTType.aiff,    UTType.wav, UTType.midi, UTType.archive, UTType.gzip, UTType.bz2,    UTType.zip, UTType.appleArchive, UTType.spreadsheet, UTType.epub

这是我的工作方式:
let supportedTypes = [myArrayFromAnyOfTheAbove]

func openDocument() {
    let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: supportedTypes, asCopy: true)
    documentPicker.delegate = self
    documentPicker.allowsMultipleSelection = false
    documentPicker.shouldShowFileExtensions = true
    present(documentPicker, animated: true, completion: nil)
}

4
let docsTypes = ["public.text",
                          "com.apple.iwork.pages.pages",
                          "public.data",
                          "kUTTypeItem",
                          "kUTTypeContent",
                          "kUTTypeCompositeContent",
                          "kUTTypeData",
                          "public.database",
                          "public.calendar-event",
                          "public.message",
                          "public.presentation",
                          "public.contact",
                          "public.archive",
                          "public.disk-image",
                          "public.plain-text",
                          "public.utf8-plain-text",
                          "public.utf16-external-plain-​text",
                          "public.utf16-plain-text",
                          "com.apple.traditional-mac-​plain-text",
                          "public.rtf",
                          "com.apple.ink.inktext",
                          "public.html",
                          "public.xml",
                          "public.source-code",
                          "public.c-source",
                          "public.objective-c-source",
                          "public.c-plus-plus-source",
                          "public.objective-c-plus-​plus-source",
                          "public.c-header",
                          "public.c-plus-plus-header",
                          "com.sun.java-source",
                          "public.script",
                          "public.assembly-source",
                          "com.apple.rez-source",
                          "public.mig-source",
                          "com.apple.symbol-export",
                          "com.netscape.javascript-​source",
                          "public.shell-script",
                          "public.csh-script",
                          "public.perl-script",
                          "public.python-script",
                          "public.ruby-script",
                          "public.php-script",
                          "com.sun.java-web-start",
                          "com.apple.applescript.text",
                          "com.apple.applescript.​script",
                          "public.object-code",
                          "com.apple.mach-o-binary",
                          "com.apple.pef-binary",
                          "com.microsoft.windows-​executable",
                          "com.microsoft.windows-​dynamic-link-library",
                          "com.sun.java-class",
                          "com.sun.java-archive",
                          "com.apple.quartz-​composer-composition",
                          "org.gnu.gnu-tar-archive",
                          "public.tar-archive",
                          "org.gnu.gnu-zip-archive",
                          "org.gnu.gnu-zip-tar-archive",
                          "com.apple.binhex-archive",
                          "com.apple.macbinary-​archive",
                          "public.url",
                          "public.file-url",
                          "public.url-name",
                          "public.vcard",
                          "public.image",
                          "public.fax",
                          "public.jpeg",
                          "public.jpeg-2000",
                          "public.tiff",
                          "public.camera-raw-image",
                          "com.apple.pict",
                          "com.apple.macpaint-image",
                          "public.png",
                          "public.xbitmap-image",
                          "com.apple.quicktime-image",
                          "com.apple.icns",
                          "com.apple.txn.text-​multimedia-data",
                          "public.audiovisual-​content",
                          "public.movie",
                          "public.video",
                          "com.apple.quicktime-movie",
                          "public.avi",
                          "public.mpeg",
                          "public.mpeg-4",
                          "public.3gpp",
                          "public.3gpp2",
                          "public.audio",
                          "public.mp3",
                          "public.mpeg-4-audio",
                          "com.apple.protected-​mpeg-4-audio",
                          "public.ulaw-audio",
                          "public.aifc-audio",
                          "public.aiff-audio",
                          "com.apple.coreaudio-​format",
                          "public.directory",
                          "public.folder",
                          "public.volume",
                          "com.apple.package",
                          "com.apple.bundle",
                          "public.executable",
                          "com.apple.application",
                          "com.apple.application-​bundle",
                          "com.apple.application-file",
                          "com.apple.deprecated-​application-file",
                          "com.apple.plugin",
                          "com.apple.metadata-​importer",
                          "com.apple.dashboard-​widget",
                          "public.cpio-archive",
                          "com.pkware.zip-archive",
                          "com.apple.webarchive",
                          "com.apple.framework",
                          "com.apple.rtfd",
                          "com.apple.flat-rtfd",
                          "com.apple.resolvable",
                          "public.symlink",
                          "com.apple.mount-point",
                          "com.apple.alias-record",
                          "com.apple.alias-file",
                          "public.font",
                          "public.truetype-font",
                          "com.adobe.postscript-font",
                          "com.apple.truetype-​datafork-suitcase-font",
                          "public.opentype-font",
                          "public.truetype-ttf-font",
                          "public.truetype-collection-​font",
                          "com.apple.font-suitcase",
                          "com.adobe.postscript-lwfn​-font",
                          "com.adobe.postscript-pfb-​font",
                          "com.adobe.postscript.pfa-​font",
                          "com.apple.colorsync-profile",
                          "public.filename-extension",
                          "public.mime-type",
                          "com.apple.ostype",
                          "com.apple.nspboard-type",
                          "com.adobe.pdf",
                          "com.adobe.postscript",
                          "com.adobe.encapsulated-​postscript",
                          "com.adobe.photoshop-​image",
                          "com.adobe.illustrator.ai-​image",
                          "com.compuserve.gif",
                          "com.microsoft.bmp",
                          "com.microsoft.ico",
                          "com.microsoft.word.doc",
                          "com.microsoft.excel.xls",
                          "com.microsoft.powerpoint.​ppt",
                          "com.microsoft.waveform-​audio",
                          "com.microsoft.advanced-​systems-format",
                          "com.microsoft.windows-​media-wm",
                          "com.microsoft.windows-​media-wmv",
                          "com.microsoft.windows-​media-wmp",
                          "com.microsoft.windows-​media-wma",
                          "com.microsoft.advanced-​stream-redirector",
                          "com.microsoft.windows-​media-wmx",
                          "com.microsoft.windows-​media-wvx",
                          "com.microsoft.windows-​media-wax",
                          "com.apple.keynote.key",
                          "com.apple.keynote.kth",
                          "com.truevision.tga-image",
                          "com.sgi.sgi-image",
                          "com.ilm.openexr-image",
                          "com.kodak.flashpix.image",
                          "com.j2.jfx-fax",
                          "com.js.efx-fax",
                          "com.digidesign.sd2-audio",
                          "com.real.realmedia",
                          "com.real.realaudio",
                          "com.real.smil",
                          "com.allume.stuffit-archive",
                          "org.openxmlformats.wordprocessingml.document",
                          "com.microsoft.powerpoint.​ppt",
                          "org.openxmlformats.presentationml.presentation",
                          "com.microsoft.excel.xls",
                          "org.openxmlformats.spreadsheetml.sheet",
                         
    
  ]
let documentPicker = UIDocumentPickerViewController(documentTypes: Utils.docsTypes, in: .import)
    documentPicker.delegate = self
    documentPicker.allowsMultipleSelection = true
    present(documentPicker, animated: true, completion: nil)

2
这是UIDocumentPickerViewController的SwiftUI版本。
感谢这篇博客文章:https://capps.tech/blog/read-files-with-documentpicker-in-swiftui 我在此发布代码,以防博客文章消失,以便保存。
(我稍微调整了代码,将所选证书文件复制并将数据写入库文件夹;在博客中,它复制了文本文件的内容。)
import SwiftUI
    
struct ContentView: View {
@State var fileContent:Data = Data()
@State var showDocumentPicker = false

var body: some View {
        
    Button() {
        showDocumentPicker = true
    } label: {
        Text("click me to show file browser")
    }
    .sheet(isPresented: self.$showDocumentPicker) {
        DocumentPicker(fileContent: $fileContent)
    }
}
}

struct DocumentPicker: UIViewControllerRepresentable {

    @Binding var fileContent: Data
    
    func makeCoordinator() -> DocumentPickerCoordinator {
        return DocumentPickerCoordinator(fileContent: $fileContent)
    }
    
    func makeUIViewController(context:
        UIViewControllerRepresentableContext<DocumentPicker>) ->
    UIDocumentPickerViewController {
        //The file types like ".pkcs12" are listed here:
        //https://developer.apple.com/documentation/uniformtypeidentifiers/system_declared_uniform_type_identifiers?changes=latest_minor
        let controller: UIDocumentPickerViewController = UIDocumentPickerViewController(forOpeningContentTypes: [.pkcs12], asCopy: true)
        controller.delegate = context.coordinator
    return controller
    }
    
    func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<DocumentPicker>) {
        print("update")
    }
} //struct

class DocumentPickerCoordinator: NSObject, UIDocumentPickerDelegate, UINavigationControllerDelegate {

    @Binding var fileContent: Data
    
    init(fileContent: Binding<Data>) {
        _fileContent = fileContent
    }

    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        let fileURL = urls[0]
        
        let certData = try! Data(contentsOf: fileURL)
        
        if let documentsPathURL = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first {
            let certURL = documentsPathURL.appendingPathComponent("certFile.pfx")
            
            try? certData.write(to: certURL)
        }

    }
}

2

我曾经遇到的问题是如何为 PickerView 指定一些特定的格式,例如 .pptx 和 .xlsx 文件。以下是一些常用类型的 PickerView 创建代码...

let types: [String] = [
    kUTTypeJPEG as String,
    kUTTypePNG as String,
    "com.microsoft.word.doc",
    "org.openxmlformats.wordprocessingml.document",
    kUTTypeRTF as String,
    "com.microsoft.powerpoint.​ppt",
    "org.openxmlformats.presentationml.presentation",
    kUTTypePlainText as String,
    "com.microsoft.excel.xls",
    "org.openxmlformats.spreadsheetml.sheet",
    kUTTypePDF as String,
    kUTTypeMP3 as String
]
let documentPicker = UIDocumentPickerViewController(documentTypes: types, in: .import)
documentPicker.delegate = self
documentPicker.modalPresentationStyle = .formSheet
self.present(documentPicker, animated: true, completion: nil)

在编制此列表时,我发现有两个地方非常有用:

https://developer.apple.com/library/archive/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html

https://escapetech.eu/manuals/qdrop/uti.html

希望能对某些人有所帮助!

1
func openiCloudDocuments(){
    let importMenu = UIDocumentPickerViewController(documentTypes: [String("public.data")], in: .import)
    importMenu.delegate = self
    importMenu.modalPresentationStyle = .formSheet
    self.present(importMenu, animated: true, completion: nil)
}

我该如何使用它来访问:.ovpn文件? - Blue_Alien

1
这将帮助您实现下载/上传功能。
UIDocumentMenuViewController *importMenu = [[UIDocumentMenuViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeImport | UIDocumentPickerModeExportToService];

了解更多,请阅读苹果文档


-1

您可以使用NSURLSession来实现您所描述的内容。

您需要将目标目录限制为您的应用程序文档目录。应用程序无法完全访问文件系统。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接