如何将@Query数组中的变量以@Binding传递给子视图?
问题:拆分列表行视图后@Query数据无法跨设备同步更新
主视图通过@Query从CloudKit获取Item数组,用ForEach渲染列表。当行视图内嵌在主视图时,其他设备更新数据后列表能自动同步;但将行视图拆分为独立的ItemRow子视图后,数据更新无法触发子视图刷新。尝试用@Binding传递item时提示类型不匹配,在ForEach内声明@Bindable var item = item也无法编译。环境为Xcode 15、iOS 17。
可行的内嵌写法:
struct ItemsView: View { @Environment(\.modelContext) private var modelContext @Query(sort: [SortDescriptor(\Item.name)]) private var items: [Item] var body: some View { NavigationStack { List { ForEach(items) { item in ZStack(alignment: .leading) { NavigationLink { EditView(item: item) } label: { EmptyView() } HStack(spacing: 8) { Image(systemName: "photo.circle.fill") .resizable() .scaledToFit() .frame(width: 48, height: 48) .opacity(0.08) VStack(alignment: .leading) { Text(item.name).font(.body) if let desc = item.description, !desc.isEmpty { Text(desc).font(.footnote) } } } } } } } } }
拆分后无法更新的写法:
struct ItemsView: View { @Environment(\.modelContext) private var modelContext @Query(sort: [SortDescriptor(\Item.name)]) private var items: [Item] var body: some View { NavigationStack { List { ForEach(items) { item in ZStack(alignment: .leading) { NavigationLink { EditView(item: item) } label: { EmptyView() } ItemRow(item: item) } } } } } } struct ItemRow: View { var item: Item var body: some View { HStack(spacing: 8) { Image(systemName: "photo.circle.fill") .resizable() .scaledToFit() .frame(width: 48, height: 48) .opacity(0.08) VStack(alignment: .leading) { Text(item.name).font(.body) if let desc = item.description, !desc.isEmpty { Text(desc).font(.footnote) } } } } }
解决方法
方法1:使用@Bindable传递绑定到子视图
在ForEach的闭包内用@Bindable包装item,将其绑定对象传递给ItemRow,子视图通过@Binding接收,确保数据变化能触发视图刷新:
修改主视图:
struct ItemsView: View { @Environment(\.modelContext) private var modelContext @Query(sort: [SortDescriptor(\Item.name)]) private var items: [Item] var body: some View { NavigationStack { List { // 在闭包内声明@Bindable包装item ForEach(items) { @Bindable var item in ZStack(alignment: .leading) { NavigationLink { EditView(item: item) } label: { EmptyView() } // 传递绑定对象$item ItemRow(item: $item) } } } } } }
修改ItemRow子视图:
struct ItemRow: View { // 用@Binding接收Item的绑定对象 @Binding var item: Item var body: some View { HStack(spacing: 8) { Image(systemName: "photo.circle.fill") .resizable() .scaledToFit() .frame(width: 48, height: 48) .opacity(0.08) VStack(alignment: .leading) { Text(item.name).font(.body) if let desc = item.description, !desc.isEmpty { Text(desc).font(.footnote) } } } } }
方法2:子视图用@ObservedObject观察Item变化
如果不需要修改Item,仅需同步展示更新,可在子视图中用@ObservedObject包装Item,让视图自动观察对象的变化:
修改ItemRow子视图:
struct ItemRow: View { // 用@ObservedObject观察Item的变化 @ObservedObject var item: Item var body: some View { HStack(spacing: 8) { Image(systemName: "photo.circle.fill") .resizable() .scaledToFit() .frame(width: 48, height: 48) .opacity(0.08) VStack(alignment: .leading) { Text(item.name).font(.body) if let desc = item.description, !desc.isEmpty { Text(desc).font(.footnote) } } } } }
主视图无需额外修改,保持原有的ItemRow(item: item)调用即可。
原理说明
@Query返回的Item对象本身是可观察对象(ObservableObject),内嵌视图时SwiftUI会自动追踪其变化;但拆分子视图后,若仅传递普通var item: Item,子视图无法感知对象内部属性的更新。@Bindable将可观察对象转换为绑定对象,通过@Binding传递后,子视图能实时响应对象属性的变化;@ObservedObject则直接让子视图订阅对象的更新事件,两种方式都能实现跨设备数据同步后的视图刷新。
内容的提问来源于stack exchange,提问作者margusholland
相关产品推荐
相关产品推荐

