在SwiftUI中显示NSMenuItem



我一直在尝试在SwiftUI视图中显示一个自定义的NSMenuItem(用于菜单管理器的预览页)。但我做不到。我认为它需要首先包装在一个菜单中,并认为可能有一种方法实用地弹出菜单,但遗憾的是,这些努力都失败了,应用程序崩溃了。

到目前为止,我的代码看起来像这样:
import Foundation
import SwiftUI
struct NSMenuItemView: NSViewRepresentable {

func makeCoordinator() -> Coordinator {
return Coordinator(self)
}

func makeNSView(context: Context) -> NSView {
let view = NSView()
let menu = NSMenu()
let item = menu.addItem(withTitle: "Do Action", action: #selector(Coordinator.valueChanged(_:)), keyEquivalent: "")
item.target = context.coordinator
view.menu = menu
return view
}
func updateNSView(_ view: NSView, context: Context) {
// App crashes here :/ 
view.menu?.popUpMenuPositioningItem(
positioning: view.menu?.item(at: 0),
at: NSPoint(x: 0, y: 0),
in: view
)
}
}
extension NSMenuItemView {
final class Coordinator: NSObject {
var parent: NSMenuItemView

init(_ parent: NSMenuItemView) {
self.parent = parent
}
@objc
func valueChanged(_ sender: NSMenuItem) {
}
}
}

我错过了什么吗?它甚至可能只是实用地显示NSMenuItem?

NSMenu舒适NSViewRepresentable,所以我认为它可能只是锻炼,并在StackOverflow上看到答案(授予日期一段时间)显示类似的代码应该工作。

没有popUpMenuPositioningItem它的工作-在某种程度上我猜-当我在视图中右键单击,菜单项出现。但是我希望能够在没有右键的情况下显示菜单,就像这样。

问题是,当视图仍在呈现时显示菜单,因此发生崩溃。为了避免这种情况,您应该在您的视图出现在屏幕上之后调用popUp(positioning:at:in)。实现的方法是使用publisher触发事件在onAppear修饰符中显示菜单,并在Coordinator中监听。这是该解决方案的示例。

struct ContentView: View {
let menuPopUpTrigger = PassthroughSubject<Void, Never>()
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
NSMenuItemView(menuPopUpTrigger)
Text("Hello, world!")
}
.padding()
.onAppear {
/// trigger an event when `onAppear` is executed
menuPopUpTrigger.send()
}
}
}
struct NSMenuItemView: NSViewRepresentable {
let base = NSView()
let menu = NSMenu()

var menuPopUpTrigger: PassthroughSubject<Void, Never>

init(_ menuPopUpTrigger: PassthroughSubject<Void, Never>) {
self.menuPopUpTrigger = menuPopUpTrigger
}

func makeCoordinator() -> Coordinator {
return Coordinator(self)
}

func makeNSView(context: Context) -> NSView {
let item = menu.addItem(withTitle: "Do Action", action: #selector(Coordinator.valueChanged(_:)), keyEquivalent: "")
item.target = context.coordinator
base.menu = menu
context.coordinator.bindTrigger(menuPopUpTrigger)
return base
}
func updateNSView(_ view: NSView, context: Context) { }
}
extension NSMenuItemView {
final class Coordinator: NSObject {
var parent: NSMenuItemView
var cancellable: AnyCancellable?

init(_ parent: NSMenuItemView) {
self.parent = parent
}
@objc func valueChanged(_ sender: NSMenuItem) { }

/// bind trigger to listen an event
func bindTrigger(_ trigger: PassthroughSubject<Void, Never>) {
cancellable = trigger
.delay(for: .seconds(0.1), scheduler: RunLoop.main)
.sink { [weak self] in
self?.parent.menu.popUp(
positioning: self?.parent.menu.item(at: 0),
at: NSPoint(x: 0, y: 0),
in: self?.parent.base
)
}
}
}
}

我希望它能帮助你得到你想要的。

最新更新