SwiftUI中使用SpriteView实现SpriteKit场景切换的问题
解决SpriteKit场景切换后后台返回丢失的问题
问题根源在于:你最初的实现中,SpriteView仅持有TestGameScene的初始引用,当通过SKView.presentScene切换到新场景后,SwiftUI并未跟踪这个场景变化。应用进入后台再返回时,SpriteView会重新初始化并使用初始的TestGameScene,导致已切换的场景被销毁。
以下是保留SpriteKit过渡效果的最佳解决方案:
方案1:用@State管理当前场景引用
通过SwiftUI的状态变量持有当前活跃场景的引用,确保后台返回时不会丢失场景实例,同时保留SpriteKit的过渡动画。
1. 修改ContentView,添加状态管理
struct ContentView: View { // 用@State存储当前展示的场景 @State private var currentScene: SKScene = TestGameScene() var body: some View { SpriteView(scene: currentScene) .onAppear { currentScene.scaleMode = .resizeFill } } }
2. 给SKScene添加切换回调
在场景类中添加闭包,用于通知SwiftUI更新当前场景引用,同时保留SpriteKit的过渡调用:
class TestGameScene: SKScene { // 定义切换回调,通知SwiftUI更新场景 var onSceneSwitch: ((SKScene) -> Void)? override func didMove(to view: SKView) { size = view.frame.size scaleMode = .resizeFill let textNode = SKLabelNode(text: "Game Scene") textNode.position = CGPoint(x: size.width/2, y: size.height/2) addChild(textNode) } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { let gameOverScene = TestGameOverScene() // 给新场景设置回调(支持从GameOver切回) gameOverScene.onSceneSwitch = { [weak self] newScene in self?.view?.presentScene(newScene, transition: .doorsOpenVertical(withDuration: 0.5)) self?.onSceneSwitch?(newScene) } // 使用SpriteKit过渡展示新场景 view?.presentScene(gameOverScene, transition: .fade(withDuration: 1.0)) // 通知SwiftUI更新当前场景引用 onSceneSwitch?(gameOverScene) } }
class TestGameOverScene: SKScene { var onSceneSwitch: ((SKScene) -> Void)? override func didMove(to view: SKView) { size = view.frame.size scaleMode = .resizeFill backgroundColor = .red let textNode = SKLabelNode(text: "Game Over") textNode.position = CGPoint(x: size.width/2, y: size.height/2) addChild(textNode) } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { let gameScene = TestGameScene() gameScene.onSceneSwitch = { [weak self] newScene in self?.view?.presentScene(newScene, transition: .doorsOpenVertical(withDuration: 0.5)) self?.onSceneSwitch?(newScene) } view?.presentScene(gameScene, transition: .fade(withDuration: 1.0)) onSceneSwitch?(gameScene) } }
方案2:用ObservableObject管理多场景(适合复杂游戏)
如果游戏场景较多,可单独用一个管理器类统一管理场景状态:
class SceneManager: ObservableObject { @Published var currentScene: SKScene init() { let initialScene = TestGameScene() initialScene.onSceneSwitch = { [weak self] newScene in self?.currentScene = newScene } self.currentScene = initialScene } } struct ContentView: View { @StateObject private var sceneManager = SceneManager() var body: some View { SpriteView(scene: sceneManager.currentScene) .onAppear { sceneManager.currentScene.scaleMode = .resizeFill } } }
核心要点
- 必须让SwiftUI持有当前场景的强引用,避免后台时场景被系统释放
- 通过闭包回调让SpriteKit场景通知SwiftUI更新状态,同时保留原生过渡动画
- 不要直接替换
SpriteView的scene参数跳过SKView.presentScene,否则会丢失SpriteKit的过渡效果
内容的提问来源于stack exchange,提问作者rayaantaneja
相关产品推荐
相关产品推荐

