You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mac端SwiftUI中SpriteView无法显示上下文菜单问题求助

SwiftUI中SpriteView上下文菜单与鼠标拖拽事件兼容解决方案

问题现象

  • Mac端SwiftUI开发时,点击SpriteView红色区域无法调出上下文菜单,但点击蓝色背景区域正常
  • 需要保留SKScene的mouseDragged()平移功能,但直接给SpriteView加overlay()会阻断事件传递到场景

解决思路

核心是让SpriteView同时响应上下文菜单和底层SKScene的鼠标事件:

  1. 开启SKScene的用户交互权限,确保mouseDragged()能正常触发
  2. 通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 22:45:52