如何使用催化剂在 ios 和 macOS 中弹出文档选择器

Posted

技术标签:

【中文标题】如何使用催化剂在 ios 和 macOS 中弹出文档选择器【英文标题】:How to popup a document picker in ios and macos using catalyst 【发布时间】:2020-03-05 15:46:06 【问题描述】:

我正在尝试使用 mac 催化剂弹出一个文档选择器,但我得到的只是一个空白屏幕。

在 iPad 和 iPhone 上的 ios 13.2.2 上一切正常,但在 macos 10.15.1 catalina 上却不行。

有谁知道如何使用催化剂在 ios 和 macos 中弹出文档选择器?

我的权利文件有:

<key>com.apple.security.app-sandbox</key>
<false/>

这是显示问题的测试代码。

import Foundation
import SwiftUI

struct ContentView: View 
@State var isFilePickerShown = false

var body: some View 
    VStack 
        Button(action:  self.isFilePickerShown.toggle() ) 
            Image(systemName: "rectangle.and.paperclip").resizable().frame(width: 70, height: 70)
        
    .sheet(isPresented: $isFilePickerShown, onDismiss: self.isFilePickerShown = false) 
        DocPickerViewController(callback: self.filePicked, onDismiss:  self.isFilePickerShown = false )
    


func filePicked(_ url: URL) 
    print("\nThe url is: \(url)")




struct DocPickerViewController: UIViewControllerRepresentable 

private let docTypes: [String] = ["com.adobe.pdf", "public.text", "public.composite-content"]
var callback: (URL) -> ()
private let onDismiss: () -> Void

init(callback: @escaping (URL) -> (), onDismiss: @escaping () -> Void) 
    self.callback = callback
    self.onDismiss = onDismiss


func makeCoordinator() -> Coordinator  Coordinator(self) 

func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<DocPickerViewController>) 


func makeUIViewController(context: Context) -> UIDocumentPickerViewController 
    let controller = UIDocumentPickerViewController(documentTypes: docTypes, in: .import)
    controller.allowsMultipleSelection = false
    controller.delegate = context.coordinator
    return controller


class Coordinator: NSObject, UIDocumentPickerDelegate 
    var parent: DocPickerViewController
    init(_ pickerController: DocPickerViewController) 
        self.parent = pickerController
    
    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) 
        parent.callback(urls[0])
        parent.onDismiss()
    
    func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) 
        parent.onDismiss()
    


我得到了下面的代码。但实际上这是 一个混乱的业务,我觉得这不是答案。

现在我在尝试显示时遇到了同样的问题 一个 UIActivityViewController。我可以使用相同的方法在 Mac 上显示它,但是 我无法控制它出现在哪里。它总是在 (0,0)。

struct FilePicker: UIViewControllerRepresentable 

private let docTypes: [String] = ["com.adobe.pdf", "public.text", "public.composite-content"]
private let controller: FilePickerController?

var callback: (URL) -> ()

private let onDismiss: () -> Void

init(callback: @escaping (URL) -> (), onDismiss: @escaping () -> Void) 
    self.callback = callback
    self.onDismiss = onDismiss
    self.controller = FilePickerController(documentTypes: docTypes, mode: .import)


func makeCoordinator() -> Coordinator 
    return Coordinator(self)


func updateUIViewController(_ uiViewController: FilePickerController, context: UIViewControllerRepresentableContext<FilePicker>)  

func makeUIViewController(context: Context) -> FilePickerController 
    return controller!


class Coordinator: NSObject, FilePickerControllerDelegate 
    var parent: FilePicker

    init(_ filePicker: FilePicker) 
        self.parent = filePicker
        super.init()
        self.parent.controller?.delegate = self
    

    func documentPicker(_ controller: FilePickerController, didPickDocumentsAt urls: [URL]) 
        parent.callback(urls[0])
        parent.onDismiss()
    

    func documentPickerWasCancelled(_ controller: FilePickerController) 
        parent.onDismiss()
    




protocol FilePickerControllerDelegate: class 
func documentPickerWasCancelled(_ controller: FilePickerController)
func documentPicker(_ controller: FilePickerController, 
didPickDocumentsAt urls: [URL])


class FilePickerController: UIViewController, UIDocumentPickerDelegate 

weak var delegate: FilePickerControllerDelegate?

let viewController: UIDocumentPickerViewController?

public init(documentTypes: [String], mode: UIDocumentPickerMode) 
    viewController = UIDocumentPickerViewController(documentTypes: documentTypes, in: mode)
    super.init(nibName: nil, bundle: nil)


required public init?(coder: NSCoder) 
    viewController = UIDocumentPickerViewController(coder: coder)
    super.init(coder: coder)


override func viewWillAppear(_ animated: Bool) 
    super.viewWillAppear(animated)
    if let viewController = viewController 
        viewController.delegate = self
        self.present(viewController, animated: animated)
    


func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) 
    self.dismiss(animated: false) 
        self.delegate?.documentPicker(self, didPickDocumentsAt: urls)
    


func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) 
    self.dismiss(animated: false) 
        self.delegate?.documentPickerWasCancelled(self)
    



我这样称呼它:

.sheet(isPresented: $isShown, onDismiss: self.isShown = false) 
        #if targetEnvironment(macCatalyst)
            FilePicker(callback: self.filePicked, onDismiss:  self.isShown = false )
        #else
            DocPickerViewController(callback: self.filePicked, onDismiss:  self.isShown = false )
        #endif
    

【问题讨论】:

你搞清楚了吗? 【参考方案1】:

解决方法:为催化剂提供选择器控制器

UIApplication.shared.firstKeyWindow?.rootViewController!.present(self.picker.controller!, animated: true)

extension UIApplication 
    public var firstKeyWindow: UIWindow? 
        windows.first(where:  $0.isKeyWindow )
    

【讨论】:

【参考方案2】:

所有代码来自hstdtworkingdog

import SwiftUI

struct ContentView: View 
    @State private var isFilePickerShown = false
    @State private var picker = DocumentPicker()

    var body: some View 
        VStack 
            Button(action: 
                self.isFilePickerShown.toggle()
                 #if targetEnvironment(macCatalyst)
                UIApplication.shared.windows[0].rootViewController!.present(self.picker.viewController, animated: true)
                #endif
            ) 
                Image(systemName: "rectangle.and.paperclip").resizable().frame(width: 70, height: 70)
            
        
        .sheet(isPresented: $isFilePickerShown, onDismiss: self.isFilePickerShown = false) 
            DocPickerViewController(callback: self.filePicked, onDismiss:  self.isFilePickerShown = false )
        
    

    func filePicked(_ url: URL) 
        print("\nThe url is: \(url)")
    

现在为 MacOS 创建新的 swiftui 文件 DocumentPicker

import SwiftUI

final class DocumentPicker: NSObject, UIViewControllerRepresentable 
    typealias UIViewControllerType = UIDocumentPickerViewController

    lazy var viewController:UIDocumentPickerViewController = 
        // For picked only folder
        let vc = UIDocumentPickerViewController(documentTypes: ["public.folder"], in: .open)
        // For picked every document
//        let vc = UIDocumentPickerViewController(documentTypes: ["public.data"], in: .open)
        // For picked only images
//        let vc = UIDocumentPickerViewController(documentTypes: ["public.image"], in: .open)
        vc.allowsMultipleSelection = false
//        vc.accessibilityElements = [kFolderActionCode]
//        vc.shouldShowFileExtensions = true
        vc.delegate = self
        return vc
    ()

    func makeUIViewController(context: UIViewControllerRepresentableContext<DocumentPicker>) -> UIDocumentPickerViewController 
        viewController.delegate = self
        return viewController
    

    func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<DocumentPicker>) 
    


extension DocumentPicker: UIDocumentPickerDelegate 
    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) 
        print(urls)
    

    func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) 
        controller.dismiss(animated: true) 
        
        print("cancelled")
    

此代码使用催化剂在 iOS 和 MacOS 中弹出一个文档选择器。 使用 MacOS Catalyst 使用 DocumentPicker 类,使用 iOS 使用 DocPickerViewController workingdog 编写(参见上面的workingdog 帖子)

【讨论】:

【参考方案3】:

这是 Cesare Piersigilli 在 GitHub 上的示例:https://github.com/AndreasPrang/Catalyst-File-Picker

【讨论】:

以上是关于如何使用催化剂在 ios 和 macOS 中弹出文档选择器的主要内容,如果未能解决你的问题,请参考以下文章

Mac 催化剂版本

如何在 macOS 上的 Cocoa 中显示警报弹出窗口?

为 mac 催化剂的 info.plist 文件中的键设置不同的值

为 mac 催化剂的 info.plist 文件中的键设置不同的值

Mac 催化剂和 iOS 13.5

在 ios 中弹出到下一个 viewController