I'm trying to popup a document picker using mac catalyst, but all I get is a blank screen.
All works well on ios 13.2.2 on iPad and iPhone, but not on macos 10.15.1 catalina.
Does anyone knows how to popup a document picker in both ios and macos using catalyst?
with my entitlements file having:
<key>com.apple.security.app-sandbox</key>
<false/>
Here is the test code that shows the problem.
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()
}
}
}
I got something going with the following code. But really this is a messy business and I feel this is not the answer.
Now I have the same problem trying to display a UIActivityViewController. I can display it on Mac using the same approach but I can't control where it shows up. It's always at (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)
}
}
}
I call it like this:
.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
}
By Tyler Lacoma February 14, 2020. Share. Project Catalyst is a big deal in Apple circles these days, and for good reason: It refers to Apple's project to merge development for iOS and Mac apps, allowing you to use them interchangeably on all kinds of Apple devices.
Mac Catalyst is the technology that lets you bring your existing iOS applications to macOS, allowing them to take full advantage of the Mac's larger display, integrated keyboard, and mouse or trackpad.
For all with code from hstdt
and workingdog
.
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)")
}
}
and now create new swiftui file DocumentPicker
for MacOS:
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")
}
}
This code popup a document picker in both iOS and MacOS using catalyst.
With MacOS Catalyst work with DocumentPicker
Class and with iOS with DocPickerViewController
write by workingdog
(see workingdog
post above)
Workaround: present picker controller for catalyst
UIApplication.shared.firstKeyWindow?.rootViewController!.present(self.picker.controller!, animated: true)
extension UIApplication {
public var firstKeyWindow: UIWindow? {
windows.first(where: { $0.isKeyWindow })
}
}
That's the example from Cesare Piersigilli on GitHub: https://github.com/AndreasPrang/Catalyst-File-Picker
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With