Mac端SwiftUI中SpriteView无法显示上下文菜单问题求助
SwiftUI中SpriteView上下文菜单与鼠标拖拽事件兼容解决方案
问题现象
- Mac端SwiftUI开发时,点击SpriteView红色区域无法调出上下文菜单,但点击蓝色背景区域正常
- 需要保留SKScene的
mouseDragged()平移功能,但直接给SpriteView加overlay()会阻断事件传递到场景
解决思路
核心是让SpriteView同时响应上下文菜单和底层SKScene的鼠标事件:
- 开启SKScene的用户交互权限,确保
mouseDragged()能正常触发 - 通过
NSViewRepresentable自定义包装SpriteView,手动处理右键事件,既唤起上下文菜单,又不阻断事件传递给SKScene
完整代码实现
import SwiftUI import SpriteKit // 自定义菜单项组件 func MenuItem(_ text: String, _ action: @escaping ()->Void) -> some View { Button(action: action) { Text(text) } } // 自定义SKScene,实现鼠标拖拽平移逻辑 class CustomScene: SKScene { override init(size: CGSize) { super.init(size: size) self.isUserInteractionEnabled = true // 必须开启才能响应鼠标事件 self.scaleMode = .aspectFill self.backgroundColor = .red // 添加相机用于平移场景 let camera = SKCameraNode() self.camera = camera self.addChild(camera) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } // 实现鼠标拖拽平移逻辑 override func mouseDragged(with event: NSEvent) { let translation = event.delta self.camera?.position.x -= translation.x self.camera?.position.y += translation.y } } // 包装SpriteView,处理右键菜单并传递事件 struct SpriteViewWithContextMenu: NSViewRepresentable { let scene: SKScene let menu: () -> MenuContent func makeNSView(context: Context) -> SKView { let skView = SKView() skView.scene = scene skView.allowsTransparency = true // 设置空菜单占位,用于触发自定义菜单逻辑 skView.menu = NSMenu() skView.menu?.addItem(withTitle: "", action: nil, keyEquivalent: "") skView.target = context.coordinator return skView } func updateNSView(_ nsView: SKView, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject { let parent: SpriteViewWithContextMenu init(_ parent: SpriteViewWithContextMenu) { self.parent = parent } // 唤起SwiftUI上下文菜单 @objc func showMenu(_ sender: Any?) { guard let skView = sender as? SKView else { return } let menuView = MenuView(menu: parent.menu) let hostingView = NSHostingView(rootView: menuView) hostingView.frame.size = hostingView.fittingSize let menu = NSMenu() let item = NSMenuItem() item.view = hostingView menu.addItem(item) skView.menu = menu skView.displayMenu(menu, at: skView.convert(NSCursor.current.position, from: nil), in: skView) } // 处理右键事件,先触发菜单再传递给场景 override func rightMouseDown(with event: NSEvent) { showMenu(event.sender) parent.scene.rightMouseDown(with: event) } } } // 包装菜单内容的容器View struct MenuView: View { let menu: () -> MenuContent var body: some View { menu() } } // 主视图 struct ContentView: View { let scene = CustomScene(size: CGSize(width: 500, height: 500)) var body: some View { ZStack { SpriteViewWithContextMenu(scene: scene) { MenuItem("1 Preferences ...") { print("菜单1被点击") } } .padding() } .background(Color.blue) .contextMenu { MenuItem("0 Preferences ...") { print("菜单0被点击") } } } }
关键说明
CustomScene中必须设置isUserInteractionEnabled = true,否则mouseDragged()无法触发;添加相机节点实现场景平移效果SpriteViewWithContextMenu通过原生NSMenu结合NSHostingView渲染SwiftUI菜单,避免使用overlay()阻断事件传递- Coordinator中重写右键事件处理逻辑,确保菜单唤起后,鼠标事件仍能正常传递给SKScene
内容的提问来源于stack exchange,提问作者andrewz
相关产品推荐
相关产品推荐

