如何实现子SpriteView向父普通View的反向通信
SpriteKit 子节点与父 SwiftUI View 反向通信方案
SpriteKit 的 SKScene 运行在独立的渲染循环中,和 SwiftUI 的视图状态树是分离的,直接给 SpriteView 传绑定变量如果没有同步到 SKScene 实例本身,就会出现修改无效的问题。以下是两种经过验证的最简实现方案:
方案1:使用 ObservableObject 共享状态(最易用,适合多组件通信场景)
适合需要在多个视图、游戏节点间共享状态的场景,无额外依赖
- 首先定义符合
ObservableObject协议的状态类,把需要共享的变量用@Published修饰
class GameState: ObservableObject { @Published var showMenu: Bool = false @Published var userScore: Int = 0 }
- 父 View 中用
@StateObject持有状态实例,注入到 SpriteView 环境中
struct ParentView: View { @StateObject private var gameState = GameState() var body: some View { ZStack { if gameState.showMenu { MenuView() // 你的菜单视图 } SpriteView(scene: GameScene()) .environmentObject(gameState) } } }
- 在 SKScene 子类中获取环境对象,修改状态即可触发父 View 刷新
class GameScene: SKScene { private var gameState: GameState? override func didMove(to view: SKView) { // 从View环境中读取共享状态实例 gameState = view.environmentObject(GameState.self) // 示例:创建可点击的SKShapeNode按钮 let backButton = SKShapeNode(rectOf: CGSize(width: 120, height: 60)) backButton.name = "backToMenu" addChild(backButton) } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first, let clickedNode = nodes(at: touch.location(in: self)).first, clickedNode.name == "backToMenu" else { return } // 直接修改共享状态,父View会自动响应更新 gameState?.showMenu = true } }
方案2:使用闭包回调(轻量,适合仅少量通知场景)
如果之前使用回调失效,大概率是没有把回调绑定到实际运行的SKScene实例上,正确写法如下:
- 给SKScene子类添加回调属性
class GameScene: SKScene { var onRequestShowMenu: (() -> Void)? override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first, let clickedNode = nodes(at: touch.location(in: self)).first, clickedNode.name == "backToMenu" else { return } // 触发回调通知父View onRequestShowMenu?() } }
- 父View创建SKScene实例时绑定回调
struct ParentView: View { @State private var showMenu = false var body: some View { ZStack { if showMenu { MenuView() } SpriteView(scene: { let scene = GameScene(size: CGSize(width: 1080, height: 1920)) // 给实际运行的SKScene实例绑定回调,不要直接绑SpriteView属性 scene.onRequestShowMenu = { showMenu = true } return scene }()) } } }
内容的提问来源于stack exchange,提问作者Fabi
相关产品推荐
相关产品推荐

