SwiftUI NavigationLink跳转时重复实例化目标视图问题
问题成因
你的判断完全正确,重复跳转3次的bug和列表共3行直接相关,核心问题是列表内所有行的NavigationLink共用了同一个激活状态绑定。
SwiftUI渲染List时,会为每一个可见行创建对应的视图实例,你写的3行列表就对应3个独立的NavigationLink实例,这3个实例全部绑定了同一个isActive计算属性、同一个destination状态。当你点击某一行修改destination值后,isActive会同步变为true,3个NavigationLink同时监听到激活状态变更,就会依次触发跳转,最终连续弹出3层目标视图。
除此之外你的代码还有两个会引发后续异常的隐患:
- 在
MenuViewOptionRowView内部直接初始化UserSettingsStore(),会导致每渲染一行就新建一个store实例,全局选中状态根本无法同步,对勾显示逻辑会失效。 - 给NavigationLink的展示内容额外添加
onTapGesture,会和NavigationLink原生的点击事件冲突,后续大概率出现点击无响应、跳转状态错乱的问题。
修复方案
SwiftUI针对列表中多跳转项的场景,原生提供了tag+selection的NavigationLink绑定方式,不需要自己手写共用的isActive绑定,修复步骤如下:
- 调整父视图
MenuViewOptions的状态持有逻辑,改用selection绑定模式管理跳转,把全局状态UserSettingsStore提到父视图统一持有:
struct MenuViewOptions: View { @State private var selectedDestination: MenuViewType? @StateObject private var userSettingsStore = UserSettingsStore() var body: some View { VStack { List(SettingsOptions().menuViewSettingsOptions) { item in NavigationLink( tag: MenuViewType(rawValue: item.id)!, selection: $selectedDestination, destination: { selectedDestination?.view } ) { MenuViewOptionRowView( menuViewItem: item, userSettingsStore: userSettingsStore, selectedDestination: $selectedDestination ) } } }.navigationBarTitle("Settings") } }
- 调整行视图
MenuViewOptionRowView,移除内部新建的store实例和多余的点击手势,通过传参接收父视图的状态,跳转前的设置项更新逻辑改用状态监听实现:
struct MenuViewOptionRowView: View { let menuViewItem: MenuViewSettingsItem @ObservedObject var userSettingsStore: UserSettingsStore @Binding var selectedDestination: MenuViewType? var body: some View { HStack { Image(systemName: menuViewItem.imageName) .circularImageStyle(width: 15, height: 15, padding: 5, backgroundColor: Color(red: 34 / 255, green: 34 / 255, blue: 34 / 255), foregroundColor: Color(red: 23 / 255, green: 121 / 255, blue: 232 / 255)) Text(menuViewItem.name) Spacer() if menuViewItem.id == userSettingsStore.menuViewSelection { Image(systemName: "checkmark").foregroundColor(Color(red: 23 / 255, green: 121 / 255, blue: 232 / 255)) } } .onChange(of: selectedDestination) { newValue in guard let selected = newValue else { return } userSettingsStore.menuViewSelection = selected.rawValue } } }
这种写法下,SwiftUI会自动匹配和当前selection值tag一致的NavigationLink触发跳转,不会出现所有链接同时响应的问题;状态统一收归父视图持有,也从根源上避免了子视图重复初始化状态对象导致的各类异常。
内容的提问来源于stack exchange,提问作者tech_human
相关产品推荐
相关产品推荐

