Swiftui UIDocumentPickerViewController不';不要在Mac Catalyst上显示任何内容

Swiftui UIDocumentPickerViewController不';不要在Mac Catalyst上显示任何内容,swiftui,mac-catalyst,Swiftui,Mac Catalyst,UIDocumentPickerViewController可以在iOS上工作,但不能在Mac Catalyst上工作。是否有其他解决此问题的方法?顺便说一句,Mac Catalyst上的NSOpenPanel不可用 import SwiftUI final class DocumentPicker: NSObject, UIViewControllerRepresentable, ObservableObject { typealias UIViewControlle

UIDocumentPickerViewController可以在iOS上工作,但不能在Mac Catalyst上工作。是否有其他解决此问题的方法?顺便说一句,Mac Catalyst上的NSOpenPanel不可用

import SwiftUI

    final class DocumentPicker: NSObject, UIViewControllerRepresentable, ObservableObject {
        typealias UIViewControllerType = UIDocumentPickerViewController
        @Published var urlsPicked = [URL]()

        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]) {
            urlsPicked = urls
            print("DocumentPicker geoFolder.geoFolderPath: \(urlsPicked[0].path)")
        }

    //    func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
    //        controller.dismiss(animated: true) {
    //        }
    //    }
    }

以下示例适用于Mac Catalyst。如果要在iOS和Mac Catalyst上支持UIDocumentPickerViewController,应使用

#if targetEnvironment(macCatalyst)
//code for Mac Catalyst
#endif
如何在Mac Catalyst上支持UIDocumentPickerViewController

//SceneDelegate.swift
import UIKit
import SwiftUI

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?
    var picker = DocumentPicker()

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    let contentView = ContentView()
        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView)
            self.window = window
            window.makeKeyAndVisible()

            window.rootViewController?.present(picker.viewController, animated: true)
        }
    }
}


//ContentView.swift
final class DocumentPicker: NSObject, UIViewControllerRepresentable {
    typealias UIViewControllerType = UIDocumentPickerViewController

    lazy var viewController: UIDocumentPickerViewController = {
        let vc = UIDocumentPickerViewController(documentTypes: ["public.data"], in: .open)
        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, completion: nil)
        print("cancelled")
    }
}
//SceneDelegate.swift
导入UIKit
导入快捷键
类SceneDelegate:UIResponder,UIWindowSceneDelegate{
变量窗口:UIWindow?
var picker=DocumentPicker()
func场景(场景:UIScene,willConnectTo会话:UISceneSession,选项connectionOptions:UIScene.connectionOptions){
让contentView=contentView()
//使用UIHostingController作为窗口根视图控制器。
如果让windowScene=场景为?UIWindowScene{
let window=UIWindow(windowScene:windowScene)
window.rootViewController=UIHostingController(rootView:contentView)
self.window=window
window.makeKeyAndVisible()的
window.rootViewController?.present(picker.viewController,动画:true)
}
}
}
//ContentView.swift
最终类文档选择器:NSObject,UIViewControllerRepresentable{
typealias UIViewControllerType=UIDocumentPickerViewController
lazy var viewController:UIDocumentPickerViewController={
让vc=UIDocumentPickerViewController(文档类型:[“public.data”],位于:。打开)
vc.delegate=self
返回vc
}()
func makeUIViewController(上下文:UIViewControllerRepresentableContext)->UIDocumentPickerViewController{
viewController.delegate=self
返回视图控制器
}
func updateUIViewController(uUIViewController:UIDocumentPickServiceWCONTROLLER,上下文:UIViewControllerRepresentableContext){
}
}
扩展文档选择器:UIDocumentPickerDelegate{
func documentPicker(uController:UIDocumentPickerViewController,didPickDocumentsAt URL:[URL]){
打印(URL)
}
取消func documentpickerwascanced(u控制器:UIDocumentPickerViewController){
控制器。解除(动画:true,完成:nil)
打印(“已取消”)
}
}

感谢Simon,没有他的帮助,我无法解决这个问题。

经过多次尝试,我终于找到了下面的代码,它可以在Mac Catalyst上与Xcode 11.3.1配合使用

import SwiftUI

    final class DocumentPicker: NSObject, UIViewControllerRepresentable, ObservableObject {
        typealias UIViewControllerType = UIDocumentPickerViewController
        @Published var urlsPicked = [URL]()

        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]) {
            urlsPicked = urls
            print("DocumentPicker geoFolder.geoFolderPath: \(urlsPicked[0].path)")
        }

    //    func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
    //        controller.dismiss(animated: true) {
    //        }
    //    }
    }

我希望我能有所帮助。

在@uncheartedworks的优秀答案中有额外的代码。这是一个更干净的版本,带有一些选项,可以在代码中进行更多的复制/粘贴。这适用于iOS、iPadOS和Mac Catalyst(无需使用#if条件)

重要提示:将“com.apple.security.files.user selected.read-write”(布尔值,设置为YES)权限添加到应用程序的权限文件中,否则当您在Mac上打开选择器时,它将崩溃。如果您只需要读取访问权限,则可以使用“com.apple.security.files.user selected.read”

示例用法:

struct ContentView: View {
    /// The view controller for the sheet that lets the user select the project support folder
    ///
    /// Yes, I said "view controller" - we need to go old school for Catalyst and present a view controller from the root view controller manually.
    @State var filePicker: DocumentPicker

    init() {
        // Setting filePicker like this lets us keep DocumentPicker in the view
        // layer (instead of a model or view model), lets SwiftUI hang onto
        // the reference to it, and lets you specify another function to
        // call (e.g. one from a view model) using info passed into your
        // init method.
        _filePicker = State(initialValue: DocumentPicker({urls in
                print(urls)
            }))
    }

    var body: some View {
        Button("Pick a folder") {
            self.presentDocumentPicker()
        }
    }

    /// Presents the document picker from the root view controller
    ///
    /// This is required on Catalyst but works on iOS and iPadOS too, so we do it this way instead of in a UIViewControllerRepresentable
    func presentDocumentPicker() {
        let viewController = UIApplication.shared.windows[0].rootViewController!
        let controller = self.filePicker.viewController
        viewController.present(controller, animated: true)
    }

}

我也有这个问题。我还没有找到解决办法…你们找到解决办法了吗?这不管用。有关更多详细信息,请参阅我的问题:在iOS中工作正常,而不是在MacOs中。阅读评论,它看起来是来自卡塔琳娜。而且Swift UI在警报时也不稳定。MacOS中的contextMenu提供了“未知的可序列化元素,将其作为可访问性元素返回可能会导致崩溃”。问题是该怎么办?警报和上下文菜单可以手工制作。但在MacOS中,选择打开文件(UIDocumentPickerViewController)和保存(UIApplication)不会起作用,手工制作要困难得多?是否有一个很快从苹果升级的解决方案,有什么好的解决办法,或者我们希望制作一个手工制作的解决方案?有什么提示吗?而且我认为很遗憾我们没有catalyst文件选择器和文档保存器/发送器(没有它是致命的)。Catalyst Mac程序(风格类似iOS的Mac程序)实际上看起来非常好。Catalyst的SwiftUI要比仅Mac的SwiftUI更容易看起来好看。此外,Mac用户一般都是iOS用户,很少有例外,他们不仅接受而且会欣赏Mac程序的发展,更希望Mac程序发展成轻微的iOS风格,这意味着它具有商业价值。如果我们只需要选取器和保存器/发送器,那么Catalyst是一个非常好的开发。这是值得努力的。是的,你必须使用Catalina。我同意你的看法,苹果应该支持此功能。请注意,你需要将“com.Apple.security.files.user selected.read-write”(布尔值,设置为“是”)权限添加到你的应用程序的权限文件中,否则当你打开选择器时它会崩溃(例如,如果你将此代码复制/粘贴到新的Xcode项目并运行它)。
struct ContentView: View {
    /// The view controller for the sheet that lets the user select the project support folder
    ///
    /// Yes, I said "view controller" - we need to go old school for Catalyst and present a view controller from the root view controller manually.
    @State var filePicker: DocumentPicker

    init() {
        // Setting filePicker like this lets us keep DocumentPicker in the view
        // layer (instead of a model or view model), lets SwiftUI hang onto
        // the reference to it, and lets you specify another function to
        // call (e.g. one from a view model) using info passed into your
        // init method.
        _filePicker = State(initialValue: DocumentPicker({urls in
                print(urls)
            }))
    }

    var body: some View {
        Button("Pick a folder") {
            self.presentDocumentPicker()
        }
    }

    /// Presents the document picker from the root view controller
    ///
    /// This is required on Catalyst but works on iOS and iPadOS too, so we do it this way instead of in a UIViewControllerRepresentable
    func presentDocumentPicker() {
        let viewController = UIApplication.shared.windows[0].rootViewController!
        let controller = self.filePicker.viewController
        viewController.present(controller, animated: true)
    }

}