SwiftUI中List内嵌GridItem时NavigationLink跳转错误问题求助
问题原因
这个是SwiftUI List组件的已知系统Bug,根源在于List底层依赖的UITableView复用机制,和你当前全局单NavigationLink的状态更新逻辑出现了时序冲突:你点击条目给viewRouter.selectedItem赋值后,List还在完成内部单元格的渲染流程,会把你刚设置的selectedItem值覆盖为当前网格最后一个完成渲染的条目,最终导致始终跳转到最后一项。
修复方案
方案1:修改NavigationLink绑定逻辑(推荐,无副作用)
废弃全局共用isActive的写法,改用NavigationLink的tag+selection绑定机制,把跳转逻辑和每个条目直接绑定,避免状态被覆盖。改动后的核心代码如下:
struct ItemsListView: View { @EnvironmentObject var viewRouter: ViewRouter // 可直接绑定到viewRouter的selectedItem,无需额外本地状态 @State private var selectedItem: Item? @SectionedFetchRequest( sectionIdentifier: SortModel.default.section, sortDescriptors: SortModel.default.descriptors, animation: .default ) private var coreData: SectionedFetchResults<String, Item> let layout = [GridItem(.adaptive(minimum: 100)), GridItem(.adaptive(minimum: 100)), GridItem(.adaptive(minimum: 100))] var body: some View { NavigationStack { // iOS15及以下替换为NavigationView List { ForEach(coreData) { section in Section(header: Text(section.id)) { LazyVGrid(columns: layout, spacing: 10) { ForEach(section) { item in NavigationLink(tag: item, selection: $selectedItem) { ItemDetailView(item: item) } label: { LabelView(item: item) } } } } } } } } }
注意需要确保Item实体遵循Hashable协议,CoreData自动生成的实体默认已符合要求。
方案2:最小改动临时规避
不想改动现有逻辑的话,只需要给选中项赋值的逻辑加个主队列异步包裹,等List的渲染流程走完再激活跳转,就能避免值被覆盖:
Button(action: { DispatchQueue.main.async { viewRouter.selectedItem = item viewRouter.detailActive = true } }, label: { LabelView(item: item) })
方案3:替换List为ScrollView
如果你的场景不需要List自带的侧滑删除、section吸顶、系统列表样式等特性,直接替换为ScrollView即可完全规避这个问题,和你测试的结果一致。
内容的提问来源于stack exchange,提问作者blu-Fox
相关产品推荐
相关产品推荐

