SwiftUI新NavigationStack结合@State异常:是Bug还是用法有误?
将SwiftUI应用迁移至使用NavigationStack(path: $visibilityStack)进行程序化导航后,发现@State变量存在异常行为:替换栈中视图时,@State会保留之前的值,而非重新初始化,同时.onAppear也不会触发。但使用旧版NavigationView配合isPresented时,视图能正确初始化并重置状态。
复现步骤
- 点击NavigationLink进入子视图
FruitView - 点击“Show other fruits”按钮,将
showMoreText设为true - 点击任意水果按钮切换视图,新视图的
showMoreText仍为true(尽管初始化时声明为false),且.onAppear未执行
完整代码示例
Fruit模型与导航控制器
class Fruit: Hashable, Identifiable { var id: String init(name: String) { self.id = name } static func == (lhs: Fruit, rhs: Fruit) -> Bool { return lhs.id == rhs.id } func hash(into hasher: inout Hasher) { hasher.combine(id) } } let fruitList = [Fruit(name: "Banana"), Fruit(name: "Strawberry"), Fruit(name: "Pineapple")] class FruitViewController: ObservableObject { @Published var visibilityStack : [Fruit] = [] func openView(fruit: Fruit) { visibilityStack.removeAll() visibilityStack.append(fruit) // visibilityStack[0] = fruit // 效果相同 } func bananaAndStrawberry() { visibilityStack.append(fruitList[0]) visibilityStack.append(fruitList[1]) } }
主ContentView(含NavigationStack)
struct ContentView: View { @StateObject private var fruitViewController = FruitViewController() var body: some View { NavigationStack(path: $fruitViewController.visibilityStack) { VStack { Button("Pile Banana and Strawberry", action: bananaAndStrawberry) .padding(40) List(fruitList) { fruit in NavigationLink(value: fruit) { Text(fruit.id) } } } .navigationDestination(for: Fruit.self) { fruit in FruitView(fruitViewController: fruitViewController, fruit: fruit) } } } func bananaAndStrawberry() { fruitViewController.bananaAndStrawberry() } }
子视图FruitView(存在@State重置问题)
struct FruitView: View { @State private var showMoreText = false @ObservedObject var fruitViewController: FruitViewController var fruit: Fruit var body: some View { Text("Selected fruit: " + fruit.id) if showMoreText { Text("The text should disappear when moving to another fruit") HStack(spacing: 10) { ForEach(fruitList) { aFruit in Button(aFruit.id) { fruitViewController.openView(fruit: aFruit) } } } } else { Button("Show other fruits", action: showButtons) } } func showButtons() { showMoreText = true } }
补充测试发现
通过栈操作进一步验证问题:
- 调用
visibilityStack.append添加3个视图,初始状态均为showMoreText = false:View 1 - state 0 View 2 - state 0 View 3 - state 0(当前显示) - 修改View 3的状态为
showMoreText = true:View 1 - state 0 View 2 - state 0 View 3 - state 1(当前显示) - 调用
visibilityStack.remove(at: 1)移除View 2,结果栈结构变为:View 1 - state 0 View 3 - state 0(当前显示)
可见实际被销毁的是栈最后一个视图,而非指定的View 2,说明NavigationStack的视图复用逻辑并非完全基于栈的结构,而是结合了模型标识进行视图实例的复用或销毁。
临时解决思路
目前可行的方案是将原本存放在@State中的状态迁移到外部的ObservableObject(如FruitViewController)中,通过绑定或同步的方式管理视图状态,确保切换视图时能正确重置状态。
内容的提问来源于stack exchange,提问作者Adkarma
相关产品推荐
相关产品推荐

