SwiftData:如何从@Query属性获取Binding适配List组件?
解决SwiftData中List无法直接绑定@Query数组的问题
问题原因
@Query返回的数组由SwiftData查询引擎管理,其投影值$persons不支持直接修改数组结构(添加/删除元素),而List(data:, editActions:, rowContent:)要求数据源支持通过Binding修改数组,因此直接传入$persons会报错。
方案一:使用ForEach + 手动处理编辑动作(推荐)
这种方式更贴合SwiftData的设计逻辑,直接通过modelContext操作数据,无需额外包装:
import SwiftUI import SwiftData struct BindableTest: View { @Environment(\.modelContext) var modelContext @Query var persons: [Person] var body: some View { VStack { Button("Add") { let person = Person(name: "test", age: 12) modelContext.insert(person) } List { ForEach($persons) { $person in VStack(alignment: .leading) { Text(person.name) Text(person.age.formatted()) } } .onDelete(perform: deletePersons) .onMove(perform: movePersons) } .toolbar { EditButton() } } } private func deletePersons(at offsets: IndexSet) { offsets.forEach { index in modelContext.delete(persons[index]) } } private func movePersons(from source: IndexSet, to destination: Int) { // 若需要持久化排序,需给Person添加排序字段(如position: Int) // 示例: // var sortedPersons = persons.sorted(by: { $0.position < $1.position }) // sortedPersons.move(fromOffsets: source, toOffset: destination) // sortedPersons.enumerated().forEach { index, person in // person.position = index // } } }
方案二:自定义Bindable数据源(适配List的editActions参数)
如果坚持使用List(data:, editActions:, rowContent:),可以通过ObservableObject包装数据源,同步SwiftData的查询结果与编辑动作:
import SwiftUI import SwiftData class PersonsDataSource: ObservableObject { @Published var items: [Person] private let modelContext: ModelContext init(items: [Person], modelContext: ModelContext) { self.items = items self.modelContext = modelContext } var bindableItems: Binding<[Person]> { $items } func handleDelete(_ indices: IndexSet) { indices.map { items[$0] }.forEach(modelContext.delete) items.remove(atOffsets: indices) } func handleMove(_ source: IndexSet, _ destination: Int) { items.move(fromOffsets: source, toOffset: destination) // 更新排序字段(需Person模型包含position属性) items.enumerated().forEach { index, person in person.position = index } } } struct BindableTest: View { @Environment(\.modelContext) var modelContext @Query var persons: [Person] @StateObject private var dataSource: PersonsDataSource init() { let container = try! ModelContainer(for: Person.self) _dataSource = StateObject(wrappedValue: PersonsDataSource(items: [], modelContext: container.mainContext)) } var body: some View { VStack { Button("Add") { let person = Person(name: "test", age: 12, position: dataSource.items.count) modelContext.insert(person) } List(dataSource.bindableItems, editActions: .all) { $person in VStack(alignment: .leading) { Text(person.name) Text(person.age.formatted()) } } .onChange(of: persons) { newPersons in dataSource.items = newPersons.sorted(by: { $0.position < $1.position }) } .onReceive(dataSource.$items) { _ in try? modelContext.save() } } } }
注意:方案二需要给Person模型添加排序字段(如position: Int),否则移动操作无法持久化。
内容的提问来源于stack exchange,提问作者Tinn_Vision
相关产品推荐
相关产品推荐

