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

iOS 16中Alert触发NavigationDestination的位置配置问题

问题解决:iOS16中Alert触发NavigationDestination的正确实现

问题场景

点击「Host Game」按钮弹出名字输入Alert,点击Alert里的「Host」按钮后需要跳转到Lobby View,但在iOS16的NavigationStack中,无法确定navigationDestination的放置位置,导致Alert触发的状态变化无法启动新视图。

核心问题

原代码将navigationDestination附加在「Host Game」按钮上,这会让导航仅在该按钮的视图上下文内响应。而Alert属于独立的视图层级,状态isGameHosted的变化无法被导航栈正确捕获。

修复方案

  1. 将navigationDestination直接放在NavigationStack的子视图范围内(比如VStack的同级位置),确保导航栈能全局监听状态变化。
  2. 避免对currentGame使用强制解包,改用可选绑定传递参数,同时确保hostGame()执行后currentGame能被正确赋值。

修改后的完整代码

struct HomeView: View {
    @State private var playerName: String = ""
    @State private var isGameHosted: Bool = false
    @State private var currentGame: Game?
    @State private var showingNameAlert: Bool = false
    @State private var gameID: String?

    var body: some View {
        NavigationStack {
            VStack(spacing: 20) {
                Text("Game")
                    .bold()
                    .font(Font.custom("Futura", size: 60))
                    .foregroundColor(.red)

                Button(action: {
                    showingNameAlert = true
                }) {
                    RoundedRectangle(cornerRadius: 8)
                        .frame(width: 300, height: 50)
                        .foregroundColor(.red)
                        .overlay(
                            Text("Host Game")
                                .foregroundColor(.white)
                                .font(Font.custom("Futura", size: 20))
                                .bold()
                        )
                }

                Button(action: {
                    print("Join Game Tapped")
                }) {
                    RoundedRectangle(cornerRadius: 8)
                        .frame(width: 300, height: 50)
                        .foregroundColor(.red)
                        .overlay(
                            Text("Join Game")
                                .foregroundColor(.white)
                                .font(Font.custom("Futura", size: 20))
                                .bold()
                        )
                }

                Spacer()
            }
            // 将navigationDestination移至NavigationStack子视图层级,全局监听状态
            .navigationDestination(isPresented: $isGameHosted) {
                // 用可选绑定避免强制解包,同时提供兜底视图
                if let game = currentGame {
                    LobbyView(game: game)
                } else {
                    Text("Failed to create game")
                        .navigationTitle("Error")
                }
            }
        }
        .alert("Enter Your Name", isPresented: $showingNameAlert) {
            TextField("Name", text: $playerName)
                .textInputAutocapitalization(.words)
            Button("Host", action: {
                guard !playerName.isEmpty else { return }
                hostGame()
                showingNameAlert = false
                isGameHosted = true
            })
            Button("Cancel", role: .cancel) { }
        } message: {
            Text("Please enter your name to host the game.")
        }
    }

    // 确保hostGame执行后currentGame被正确赋值
    private func hostGame() {
        currentGame = Game(playerName: playerName, gameID: UUID().uuidString)
        gameID = currentGame?.gameID
    }
}

// 示例Game和LobbyView定义
struct Game {
    var playerName: String
    var gameID: String
}

struct LobbyView: View {
    let game: Game
    var body: some View {
        Text("Lobby for \(game.playerName)'s game (ID: \(game.gameID))")
            .navigationTitle("Lobby")
    }
}

关键说明

  • navigationDestination必须放在NavigationStack直接包含的视图上,这样无论状态从Alert还是其他地方修改,导航栈都能实时检测到变化。
  • 用guard !playerName.isEmpty else { return }替代原有的空字符串判断,代码逻辑更严谨。
  • 增加currentGame的可选绑定处理,避免强制解包引发崩溃,同时补充了错误兜底视图。

内容的提问来源于stack exchange,提问作者Zac Liebregts

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 06:04:57