SwiftUI导航时类重复初始化问题及优化方案咨询
问题现状
当前使用NavigationStack构建的游戏菜单中,每次进入/返回GameMenuView,或在菜单与GameScreen间往返时,4个NewGame实例都会重复初始化,既造成冗余加载,又导致返回时游戏状态丢失。原代码如下:
struct GameMenuView: View { @Environment(\.presentationMode) var mode: Binding<PresentationMode> var body: some View { VStack{ ButtonGame(game_name: "Title", view: AnyView(GameScreen(game: NewGame(count: 4, category: "All")))) ButtonGame(game_name: "Title", view: AnyView(GameScreen(game: NewGame(count: 6, category: "2")))) ButtonGame(game_name: "Title", view: AnyView(GameScreen(game: NewGame(count: 4, category: "None")))) ButtonGame(game_name: "Title", view: AnyView(GameScreen(game: NewGame(count: 4, category: "Basic")))) } } }
核心原因
- 直接在
ButtonGame的view参数中初始化GameScreen和NewGame,会导致GameMenuView的body每次刷新(如导航状态变化)时重新创建所有实例。 - 未对游戏状态进行持久化持有,返回时实例被销毁,状态丢失。
优化方案
1. 延迟实例化:仅点击时创建NewGame
修改ButtonGame的实现,让它接受闭包形式的目标视图,而非直接传入已初始化的View。这样只有用户点击按钮时,才会执行闭包创建GameScreen和NewGame实例。
首先调整ButtonGame结构体:
struct ButtonGame: View { let game_name: String let destination: () -> AnyView // 改为闭包类型 var body: some View { NavigationLink(destination: destination()) { // 执行闭包返回目标视图 Text(game_name) // 保留你原有的按钮样式逻辑 } } }
然后修改GameMenuView的调用方式:
struct GameMenuView: View { @Environment(\.presentationMode) var mode: Binding<PresentationMode> var body: some View { VStack{ ButtonGame(game_name: "All (4)", destination: { AnyView(GameScreen(game: NewGame(count: 4, category: "All"))) }) ButtonGame(game_name: "Category 2 (6)", destination: { AnyView(GameScreen(game: NewGame(count: 6, category: "2"))) }) ButtonGame(game_name: "None (4)", destination: { AnyView(GameScreen(game: NewGame(count: 4, category: "None"))) }) ButtonGame(game_name: "Basic (4)", destination: { AnyView(GameScreen(game: NewGame(count: 4, category: "Basic"))) }) } } }
2. 保留游戏状态(可选)
如果需要返回菜单后再次进入同一游戏时保留之前的状态,可以将NewGame改造为ObservableObject,并在父视图中持有实例,点击时复用:
首先改造NewGame:
class NewGame: ObservableObject { let count: Int let category: String @Published var currentScore: Int = 0 // 示例状态属性,根据你的游戏逻辑调整 init(count: Int, category: String) { self.count = count self.category = category // 原有的初始化逻辑 } // 原有的游戏逻辑方法 }
然后在GameMenuView中持有各个游戏的实例,点击时直接传入:
struct GameMenuView: View { @Environment(\.presentationMode) var mode: Binding<PresentationMode> // 提前创建并持有游戏实例,仅初始化一次 @StateObject private var allGame = NewGame(count: 4, category: "All") @StateObject private var category2Game = NewGame(count: 6, category: "2") @StateObject private var noneGame = NewGame(count: 4, category: "None") @StateObject private var basicGame = NewGame(count: 4, category: "Basic") var body: some View { VStack{ ButtonGame(game_name: "All (4)", destination: { AnyView(GameScreen(game: allGame)) }) ButtonGame(game_name: "Category 2 (6)", destination: { AnyView(GameScreen(game: category2Game)) }) ButtonGame(game_name: "None (4)", destination: { AnyView(GameScreen(game: noneGame)) }) ButtonGame(game_name: "Basic (4)", destination: { AnyView(GameScreen(game: basicGame)) }) } } }
这样每次进入对应游戏时,都会复用已有的NewGame实例,状态不会丢失;且仅在GameMenuView初始化时创建一次实例,避免冗余加载。
内容的提问来源于stack exchange,提问作者jesse voorn
相关产品推荐
相关产品推荐

