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

SwiftUI导航时类重复初始化问题及优化方案咨询

SwiftUI NavigationStack 游戏菜单初始化与状态优化

问题现状

当前使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 01:10:24