iOS 16中Alert触发NavigationDestination的位置配置问题
问题场景
点击「Host Game」按钮弹出名字输入Alert,点击Alert里的「Host」按钮后需要跳转到Lobby View,但在iOS16的NavigationStack中,无法确定navigationDestination的放置位置,导致Alert触发的状态变化无法启动新视图。
核心问题
原代码将navigationDestination附加在「Host Game」按钮上,这会让导航仅在该按钮的视图上下文内响应。而Alert属于独立的视图层级,状态isGameHosted的变化无法被导航栈正确捕获。
修复方案
- 将
navigationDestination直接放在NavigationStack的子视图范围内(比如VStack的同级位置),确保导航栈能全局监听状态变化。 - 避免对
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
相关产品推荐
相关产品推荐

