SwiftData中带过滤谓词的@Query导致SwiftUI导航功能异常
问题分析与解决方案
核心原因
带过滤条件的@Query会触发更频繁的视图刷新,而你通过计算属性创建的isEditing Binding每次body重建都会生成新实例,导致navigationDestination的状态绑定逻辑混乱:既无法稳定触发跳转,又会因状态反复变化引发body持续重建,出现卡顿现象。无过滤条件时视图刷新频率低,这个问题被掩盖了。
解决方案
推荐两种修复方式,第二种更符合SwiftUI + SwiftData的最佳实践:
方式1:修复Binding的状态管理
将isEditing从计算属性Binding改为独立的@State变量,避免每次body重建时生成新的Binding实例:
struct ListItemView: View { @Environment(\.modelContext) var modelContext @Query(filter: #Predicate<Item>{ $0.name != ""}) private var items: [Item] @State private var itemToEdit: Item? @State private var isEditing = false // 改为独立的State变量 var body: some View { NavigationStack{ List(items) { item in Text(item.name) .onTapGesture { itemToEdit = item isEditing = true // 手动触发编辑状态 } } } .navigationDestination(isPresented: $isEditing, onDismiss: { itemToEdit = nil // 关闭后重置编辑项 }) { if let item = itemToEdit { EditView(item: item) } } } }
方式2:使用navigationDestination(for:)(推荐)
直接绑定Item类型的导航目标,无需维护额外的布尔状态,逻辑更简洁且适配SwiftData的数据源变化:
struct ListItemView: View { @Environment(\.modelContext) var modelContext @Query(filter: #Predicate<Item>{ $0.name != ""}) private var items: [Item] var body: some View { NavigationStack{ List(items) { item in // 直接用NavigationLink绑定Item,自动匹配导航目标 NavigationLink(value: item) { Text(item.name) } } // 注册Item类型的导航目标 .navigationDestination(for: Item.self) { item in EditView(item: item) } } } }
补充说明
带过滤条件的@Query会在数据变化时重新评估谓词,导致items数组的更新频率高于无过滤的情况,这会放大原有状态管理的缺陷。使用SwiftUI原生的导航目标绑定方式(navigationDestination(for:))能更好地适配这种动态数据源的场景。
内容的提问来源于stack exchange,提问作者Rajiv Nair
相关产品推荐
相关产品推荐

