You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 11:03:20