UIDocumentPickerViewController在iOS上有效,但在Mac Catalyst上无效。是否有其他替代方法可以解决此问题?顺便说一句,Mac Catalyst上没有NSOpenPanel。
UIDocumentPickerViewController在Mac Catalyst上不显示任何内容
•
问答
skcheung 回答:UIDocumentPickerViewController在Mac Catalyst上不显示任何内容
@UnchartedWorks的出色答案中还有其他代码。这是一个更干净的版本,带有一些选项,可以将更多内容复制/粘贴到您的代码中。此功能可在iOS,iPadOS和Mac Catalyst上运行(无需使用#if条件)。
import Foundation
import SwiftUI
import MobileCoreServices
/// A wrapper for a UIDocumentPickerViewController that acts as a delegate and passes the selected file to a callback
///
/// DocumentPicker also sets allowsMultipleSelection to `false`.
final class DocumentPicker: NSObject {
/// The types of documents to show in the picker
let types: [String]
/// The callback to call with the selected document URLs
let callback: ([URL]) -> ()
/// Should the user be allowed to select more than one item?
let allowsMultipleSelection: Bool
/// Creates a DocumentPicker,defaulting to selecting folders and allowing only one selection
init(for types: [String] = [String(kUTTypeFolder)],allowsMultipleSelection: Bool = false,_ callback: @escaping ([URL]) -> () = { _ in }) {
self.types = types
self.allowsMultipleSelection = allowsMultipleSelection
self.callback = callback
}
/// Returns the view controller that must be presented to display the picker
lazy var viewController: UIDocumentPickerViewController = {
let vc = UIDocumentPickerViewController(documentTypes: types,in: .open)
vc.delegate = self
vc.allowsMultipleSelection = self.allowsMultipleSelection
return vc
}()
}
extension DocumentPicker: UIDocumentPickerDelegate {
/// Delegate method that's called when the user selects one or more documents or folders
///
/// This method calls the provided callback with the URLs of the selected documents or folders.
func documentPicker(_ controller: UIDocumentPickerViewController,didPickDocumentsAt urls: [URL]) {
callback(urls)
}
/// Delegate method that's called when the user cancels or otherwise dismisses the picker
///
/// Does nothing but close the picker.
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
controller.dismiss(animated: true,completion: nil)
print("cancelled")
}
}
重要提示:将“ 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)
}
}
,
以下示例适用于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")
}
}
感谢西蒙,没有他的帮助,我无法解决这个问题。
,经过多次尝试,我设法找到了下面的代码,它们与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) {
// }
// }
}
我使用上面的代码,例如:
import SwiftUI
struct ContentView: View {
@ObservedObject var picker = DocumentPicker()
@State private var urlPick = ""
var body: some View {
HStack {
urlPickedFoRTextField()
.textFieldStyle(RoundedBorderTextFieldStyle())
Spacer()
Button(action: {
#if targetEnvironment(macCatalyst)
let viewController = UIApplication.shared.windows[0].rootViewController!
viewController.present(self.picker.viewController,animated: true)
self.picker.objectWillChange.send()
#endif
print("Hai premuto il pulsante per determinare il path della GeoFolder")
}) {
Image(systemName: "square.and.arrow.up")
}
}
.padding()
}
private func urlPickedFoRTextField() -> some View {
if picker.urlsPicked.count > 0 {
DispatchQueue.main.async {
self.urlPick = self.picker.urlsPicked[0].path
}
}
return TextField("",text: $urlPick)
}
}
我希望我能帮上忙。