SwiftUI在MacOS中使用NSSharingServicePicker

6

我想在SwiftUI中的我的MacOS应用程序中使用共享功能。 我有一个指向文件的URL,我想要分享它。 它可以是图像/文档等等。

我找到了专为MacOS设计的NSSharingServicePicker,并希望使用它。 但是,我在SwiftUI中使用它时遇到了困难。

根据文档,我将其创建如下:

let shareItems = [...]

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

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

我的问题出在show()方法。我需要设置一个NSRect,其中可以使用NSZeroRect.. 但是我正在为of:参数而奋斗。它需要一个NSView。我如何将我的当前视图转换为NSView并以这种方式使用它。或者我可以使用我的Button作为NSView()。我正在努力应对这种方法。

另一种选择是使用NSViewRepresentable。但是我应该只创建一个NSView并将其用于该方法吗。

2个回答

13

这是最小化工作演示示例

演示

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
        }
    }
}

使用示例:

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

1
非常好用 :) 非常感谢。顺便问一下,你知道是否还有类似Finder中的“打开方式”上下文菜单可用吗? - davidev
@davidev 你找到解决方法了吗? - DoTryCatch
@Asperi,你有适用于Mac Catalyst的代码吗? - Dc7
@davidev,你需要构建自己的上下文菜单,并提供以下函数所需的文件URL和要启动的应用程序URL。NSWorkspace.shared.open(fileUrlArray, withApplicationAt: appUrl, configuration:NSWorkspace.OpenConfiguration()) - Chris Macke

0

另一种不使用NSViewRepresentable的选项是:

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")
            }
        )
    }
}

你会失去像“更多”菜单项或最近的收件人之类的东西。但在我看来,这已经足够了,简单而纯粹的SwiftUI。


1
快到了,缺少一些项目。 - iOS Blacksmith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接