SwiftUI NavigationStack:列表视图导航异常原因及解决方法
问题原因
你遇到的异常是由于**navigationDestination修饰符的注册上下文与NavigationLink(value:)的触发上下文不匹配**导致的:
- 根导航栈(
NavigationStack)位于ViewA中,ViewB是通过闭包形式的NavigationLink被推入该根栈的。 - ViewB中的
navigationDestination附着在List视图上,虽会被注册到根导航栈,但List的视图特性会导致导航目的地的注册时机与跳转触发时机产生冲突。 - 点击
NavigationLink(value:)时,根导航栈尝试匹配Item类型的目的地并推入ViewC,但此时ViewB的视图层级触发重新渲染,导致导航栈被错误修改,最终出现ViewC短暂显示后,ViewB重新回到栈顶,形成[ViewA, ViewC, ViewB]的异常栈结构。
解决方法
有两种可靠的解决方式,可根据需求选择:
方式一:改用闭包形式的NavigationLink(推荐)
这种方式无需注册navigationDestination,直接在NavigationLink中指定目标视图,彻底避免上下文匹配问题:
struct ViewB: View { let items: [Item] = [ Item(title: "Item 1", description: "This is item 1"), Item(title: "Item 2", description: "This is item 2") ] var body: some View { List(items, id: \.id) { item in // 替换为闭包形式的NavigationLink NavigationLink(item.title) { ViewC(item: item) } } } }
方式二:将navigationDestination注册到根导航栈
如果坚持使用value+navigationDestination的模式,需将目的地注册到根导航栈(ViewA的NavigationStack中),确保上下文统一:
修改ViewA的代码:
struct ViewA: View { var body: some View { NavigationStack { NavigationLink("Go to ViewB") { ViewB() } // 将navigationDestination注册到根导航栈 .navigationDestination(for: Item.self) { item in ViewC(item: item) } } } }
同时移除ViewB中的navigationDestination修饰符:
struct ViewB: View { let items: [Item] = [ Item(title: "Item 1", description: "This is item 1"), Item(title: "Item 2", description: "This is item 2") ] var body: some View { List(items, id: \.id) { item in NavigationLink(value: item) { Text(item.title) } } } }
两种方式均可让导航栈保持预期的[ViewA, ViewB, ViewC]结构,解决跳转后立即返回的异常问题。
内容的提问来源于stack exchange,提问作者Ci Leong
相关产品推荐
相关产品推荐

