简体   繁体   中英

SwiftUI using NSSharingServicePicker in MacOS

I am trying to use a Share function inside my MacOS app in SwiftUI. I am having a URL to a file, which I want to share. It can be images/ documents and much more.

I found NSSharingServicePicker for MacOS and would like to use it. However, I am struggeling to use it in SwiftUI.

Following the documentation, I am creating it like this:

let shareItems = [...]

let sharingPicker : NSSharingServicePicker = NSSharingServicePicker.init(items: shareItems as [Any])

sharingPicker.show(relativeTo: NSZeroRect, of:shareView, preferredEdge: .minY)

My problem is in that show() method. I need to set a NSRect, where I can use NSZeroRect.. but I am struggeling with of: parameter. It requires a NSView. How can I convert my current view as NSView and use it that way. Or can I use my Button as NSView() . I am struggling with that approach.

Another option would be to use a NSViewRepresentable . But should I just create a NSView and use it for that method.

Here is minimal working demo example

演示

struct SharingsPicker: NSViewRepresentable {
    @Binding var isPresented: Bool
    var sharingItems: [Any] = []

    func makeNSView(context: Context) -> NSView {
        let view = NSView()
        return view
    }

    func updateNSView(_ nsView: NSView, context: Context) {
        if isPresented {
            let picker = NSSharingServicePicker(items: sharingItems)
            picker.delegate = context.coordinator

            // !! MUST BE CALLED IN ASYNC, otherwise blocks update
            DispatchQueue.main.async {
                picker.show(relativeTo: .zero, of: nsView, preferredEdge: .minY)
            }
        }
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(owner: self)
    }

    class Coordinator: NSObject, NSSharingServicePickerDelegate {
        let owner: SharingsPicker

        init(owner: SharingsPicker) {
            self.owner = owner
        }

        func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, didChoose service: NSSharingService?) {

            // do here whatever more needed here with selected service

            sharingServicePicker.delegate = nil   // << cleanup
            self.owner.isPresented = false        // << dismiss
        }
    }
}

Demo of usage:

struct TestSharingService: View {
    @State private var showPicker = false
    var body: some View {
        Button("Share") {
            self.showPicker = true
        }
        .background(SharingsPicker(isPresented: $showPicker, sharingItems: ["Message"]))
    }
}

backup

Another option without using NSViewRepresentable is:

extension NSSharingService {
    static func submenu(text: String) -> some View {
        return Menu(
            content: {
                ForEach(items, id: \.title) { item in
                    Button(action: { item.perform(withItems: [text]) }) {
                        Image(nsImage: item.image)
                        Text(item.title)
                    }
                }
            },
            label: {
                Image(systemName: "square.and.arrow.up")
            }
        )
    }
}

You lose things like the "more" menu item or recent recipients. But in my opinion it's more than enough, simple and pure SwiftUI.

mac os monterey 12.4 , swiftUI, Xcode 13.4.1, Using Asperi's method, the picker comes up, but seems to not register the pick, repeat multiple times as picker keeps coming up, but eventually works but has created many shares of what was selected (like message). Not sure what is going wrong!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM