SwiftData中如何动态修改@Query的排序与过滤条件?
SwiftData 实现待办事项动态排序与过滤
1. 定义排序与过滤枚举
用枚举封装排序规则和过滤逻辑,让代码更易维护:
// 排序选项枚举 enum SortOption: CaseIterable { case inputOrder, alphabetical, dueDate, completionStatus // 返回对应排序规则 var sortDescriptors: [SortDescriptor<ToDoItem>] { switch self { case .inputOrder: return [SortDescriptor(\.sortOrder, order: .forward)] // 用sortOrder维护输入/拖动顺序 case .alphabetical: return [SortDescriptor(\.title, order: .forward)] case .dueDate: return [SortDescriptor(\.dueDate, order: .forward)] case .completionStatus: return [SortDescriptor(\.isCompleted, order: .forward)] // 先显示未完成项 } } // 界面显示名称 var displayName: String { switch self { case .inputOrder: return "输入顺序" case .alphabetical: return "字母顺序" case .dueDate: return "截止日期" case .completionStatus: return "完成状态" } } } // 过滤选项枚举 enum FilterOption: CaseIterable { case all, completed, notCompleted // 返回对应过滤谓词(nil表示无过滤) var predicate: Predicate<ToDoItem>? { switch self { case .all: return nil case .completed: return #Predicate<ToDoItem> { $0.isCompleted } case .notCompleted: return #Predicate<ToDoItem> { !$0.isCompleted } } } // 界面显示名称 var displayName: String { switch self { case .all: return "全部" case .completed: return "已完成" case .notCompleted: return "未完成" } } }
2. 调整SwiftData模型
确保ToDoItem包含排序和过滤所需的核心属性:
@Model class ToDoItem { var title: String var isCompleted: Bool var sortOrder: Int // 核心:维护输入/拖动后的顺序 var dueDate: Date? init(title: String, isCompleted: Bool = false, sortOrder: Int, dueDate: Date? = nil) { self.title = title self.isCompleted = isCompleted self.sortOrder = sortOrder self.dueDate = dueDate } }
3. 动态更新@Query的排序与谓词
在视图中用@State管理选中的排序/过滤选项,通过onChange回调重新初始化@Query实现动态刷新:
struct ToDoListView: View { @Environment(\.modelContext) private var modelContext @State private var selectedSort: SortOption = .inputOrder @State private var selectedFilter: FilterOption = .all // 动态初始化@Query @Query private var toDos: [ToDoItem] init() { let initialSort = SortOption.inputOrder.sortDescriptors let initialFilter = FilterOption.all.predicate _toDos = Query(filter: initialFilter, sort: initialSort) } var body: some View { NavigationStack { List { ForEach(toDos) { todo in HStack { Text(todo.title) Spacer() if let dueDate = todo.dueDate { Text(dueDate.formatted(date: .numeric, time: .shortened)) } Image(systemName: todo.isCompleted ? "checkmark.circle.fill" : "circle") } } .onMove(perform: reorderItems) // 支持拖动排序 } .toolbar { // 排序分段控件 Picker("排序", selection: $selectedSort) { ForEach(SortOption.allCases, id: \.self) { option in Text(option.displayName).tag(option) } } .pickerStyle(.segmented) // 过滤分段控件 Picker("过滤", selection: $selectedFilter) { ForEach(FilterOption.allCases, id: \.self) { option in Text(option.displayName).tag(option) } } .pickerStyle(.segmented) EditButton() // 保留编辑功能(删除、拖动) } // 排序选项变化时更新Query .onChange(of: selectedSort) { newSort in _toDos = Query(filter: selectedFilter.predicate, sort: newSort.sortDescriptors) } // 过滤选项变化时更新Query .onChange(of: selectedFilter) { newFilter in _toDos = Query(filter: newFilter.predicate, sort: selectedSort.sortDescriptors) } } } // 拖动排序逻辑:更新sortOrder并保存到SwiftData private func reorderItems(from source: IndexSet, to destination: Int) { var updatedList = toDos updatedList.move(fromOffsets: source, toOffset: destination) for (index, item) in updatedList.enumerated() { item.sortOrder = index } try? modelContext.save() } }
关键说明
@Query支持动态重新赋值:排序/过滤选项变化时,重新创建Query实例并赋值给_toDos,SwiftUI会自动刷新列表。- 拖动排序依赖
sortOrder:拖动后更新项的sortOrder值并保存,@Query会根据新的排序规则重绘列表。 - 类型安全谓词:使用
#Predicate替代字符串格式,避免语法错误,提升代码可靠性。
内容的提问来源于stack exchange,提问作者Gallaugher
相关产品推荐
相关产品推荐

