簡體   English   中英

如何從我的 iOS 應用程序中訪問 iCloud Drive 中的文件?

[英]How to access files in iCloud Drive from within my iOS app?

有沒有辦法從 iCloud Drive 中選擇文件,類似於UIImagePickerController()

您可以通過以下方式呈現控制器:

import MobileCoreServices

let documentPickerController = UIDocumentPickerViewController(documentTypes: [String(kUTTypePDF), String(kUTTypeImage), String(kUTTypeMovie), String(kUTTypeVideo), String(kUTTypePlainText), String(kUTTypeMP3)], inMode: .Import)
documentPickerController.delegate = self
presentViewController(documentPickerController, animated: true, completion: nil)

在您的委托中實現該方法:

func documentPicker(controller: UIDocumentPickerViewController, didPickDocumentAtURL url: NSURL)

請注意,您無需設置 iCloud 授權即可使用UIDocumentPickerViewController Apple在此處提供了演示如何使用此控制器的示例代碼

當用戶選擇應用程序沙箱之外的目的地時,文檔選擇器會調用委托的 documentPicker:didPickDocumentAtURL: 方法。 系統將您的文檔副本保存到指定的目的地。 文檔選擇器提供副本的 URL 以指示成功; 但是,您的應用程序無權訪問此 URL 引用的文件。 關聯

這段代碼對我有用:

func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        let url = urls[0]
        let isSecuredURL = url.startAccessingSecurityScopedResource() == true
        let coordinator = NSFileCoordinator()
        var error: NSError? = nil
        coordinator.coordinate(readingItemAt: url, options: [], error: &error) { (url) -> Void in
            _ = urls.compactMap { (url: URL) -> URL? in
                // Create file URL to temporary folder
                var tempURL = URL(fileURLWithPath: NSTemporaryDirectory())
                // Apend filename (name+extension) to URL
                tempURL.appendPathComponent(url.lastPathComponent)
                do {
                    // If file with same name exists remove it (replace file with new one)
                    if FileManager.default.fileExists(atPath: tempURL.path) {
                        try FileManager.default.removeItem(atPath: tempURL.path)
                    }
                    // Move file from app_id-Inbox to tmp/filename
                    try FileManager.default.moveItem(atPath: url.path, toPath: tempURL.path)


                    YourFunction(tempURL)
                    return tempURL
                } catch {
                    print(error.localizedDescription)
                    return nil
                }
            }
        }
        if (isSecuredURL) {
            url.stopAccessingSecurityScopedResource()
        }

        navigationController?.dismiss(animated: true, completion: nil)
    }

斯威夫特 4.X

您需要在 XCode 功能中啟用iCloud權利。 此外,您必須在 Apple 的開發者帳戶中的應用程序包中打開iCloud 完成此操作后,您可以通過以下方式呈現文檔選擇器控制器:

使用UIDocumentPickerDelegate方法

extension YourViewController : UIDocumentMenuDelegate, UIDocumentPickerDelegate,UINavigationControllerDelegate {

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

    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {
        print("url = \(url)")
    }

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

為按鈕操作添加以下代碼

@IBAction func didPressAttachment(_ sender: UIButton) {

        let importMenu = UIDocumentMenuViewController(documentTypes: [String(kUTTypePDF)], in: .import)
        importMenu.delegate = self
        importMenu.modalPresentationStyle = .formSheet

        if let popoverPresentationController = importMenu.popoverPresentationController {
            popoverPresentationController.sourceView = sender
            // popoverPresentationController.sourceRect = sender.bounds
        }
         self.present(importMenu, animated: true, completion: nil)

    }

這對我來說很好用。 希望對你也有幫助。

快樂編碼:)

這在iOS 14 中再次改變了!!

JSON 的工作示例:

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)
}

斯威夫特 5,iOS 13

Jhonattan 和 Ashu 的答案對於核心功能來說絕對是正確的,多文檔選擇、錯誤結果和不推薦使用的文檔選擇器 API 存在許多問題。

下面的代碼顯示了一個常見用例的現代自始至終版本:選擇一個外部 iCloud 文檔以導入應用程序並對其進行處理

請注意,您必須將應用程序的功能設置為使用 iCloud 文檔,並在應用程序的 .plist 中設置一個無處不在的容器...參見示例: Swift write/save/move a document file to iCloud drive

class ViewController: UIViewController {
    
    @IBAction func askForDocument(_ sender: Any) {
        
        if FileManager.default.url(forUbiquityContainerIdentifier: nil) != nil {

            let iOSPickerUI = UIDocumentPickerViewController(documentTypes: ["public.text"], in: .import)
            iOSPickerUI.delegate = self
            iOSPickerUI.modalPresentationStyle = .formSheet
            
            if let popoverPresentationController = iOSPickerUI.popoverPresentationController {
                popoverPresentationController.sourceView = sender as? UIView
            }
            self.present(iOSPickerUI, animated: true, completion: nil)
        }
    }

    func processImportedFileAt(fileURL: URL) {
        // ...
    }
}

extension ViewController: UIDocumentPickerDelegate, UINavigationControllerDelegate {
    
    func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
        dismiss(animated: true, completion: nil)
    }
    
    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        if controller.allowsMultipleSelection {
            print("WARNING: controller allows multiple file selection, but coordinate-read code here assumes only one file chosen")
            // If this is intentional, you need to modify the code below to do coordinator.coordinate
            // on MULTIPLE items, not just the first one
            if urls.count > 0 { print("Ignoring all but the first chosen file") }
        }
        
        let firstFileURL = urls[0]
        let isSecuredURL = (firstFileURL.startAccessingSecurityScopedResource() == true)
        
        print("UIDocumentPickerViewController gave url = \(firstFileURL)")

        // Status monitoring for the coordinate block's outcome
        var blockSuccess = false
        var outputFileURL: URL? = nil

        // Execute (synchronously, inline) a block of code that will copy the chosen file
        // using iOS-coordinated read to cooperate on access to a file we do not own:
        let coordinator = NSFileCoordinator()
        var error: NSError? = nil
        coordinator.coordinate(readingItemAt: firstFileURL, options: [], error: &error) { (externalFileURL) -> Void in
                
            // WARNING: use 'externalFileURL in this block, NOT 'firstFileURL' even though they are usually the same.
            // They can be different depending on coordinator .options [] specified!
        
            // Create file URL to temp copy of file we will create:
            var tempURL = URL(fileURLWithPath: NSTemporaryDirectory())
            tempURL.appendPathComponent(externalFileURL.lastPathComponent)
            print("Will attempt to copy file to tempURL = \(tempURL)")
            
            // Attempt copy
            do {
                // If file with same name exists remove it (replace file with new one)
                if FileManager.default.fileExists(atPath: tempURL.path) {
                    print("Deleting existing file at: \(tempURL.path) ")
                    try FileManager.default.removeItem(atPath: tempURL.path)
                }
                
                // Move file from app_id-Inbox to tmp/filename
                print("Attempting move file to: \(tempURL.path) ")
                try FileManager.default.moveItem(atPath: externalFileURL.path, toPath: tempURL.path)
                
                blockSuccess = true
                outputFileURL = tempURL
            }
            catch {
                print("File operation error: " + error.localizedDescription)
                blockSuccess = false
            }
            
        }
        navigationController?.dismiss(animated: true, completion: nil)
        
        if error != nil {
            print("NSFileCoordinator() generated error while preparing, and block was never executed")
            return
        }
        if !blockSuccess {
            print("Block executed but an error was encountered while performing file operations")
            return
        }
        
        print("Output URL : \(String(describing: outputFileURL))")
        
        if (isSecuredURL) {
            firstFileURL.stopAccessingSecurityScopedResource()
        }
        
        if let out = outputFileURL {
            processImportedFileAt(fileURL: out)
        }
    }

}

iCloudUrl.startAccessingSecurityScopedResource() // 此時為我返回 true,

但是下面的代碼給出了錯誤:

試試 FileManager.default.createDirectory(atPath: iCloudUrl, withIntermediateDirectories: true, attributes: nil)

“您無法保存文件“xyz”,因為該卷是只讀的。”

這實際上有效:
嘗試 FileManager.default.createDirectory(at: iCloudUrl, withIntermediateDirectories: true, attributes: nil)

這是有道理的,因為 URL 可能帶有它的安全訪問權限,但是這個小小的疏忽讓我難住了半天……

對於我的 swiftUI 用戶:這很容易。

struct HomeView: View {
    
    @State private var showActionSheet = false
    
    var body: some View {
        ButtonComponentView(title: "Press", handler: {
            showActionSheet = true
        })
         .fileImporter(isPresented: $showActionSheet, allowedContentTypes: [.data]) { (res) in
                print("!!!\(res)")
            }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM