SwiftUI中如何将@State选择器值绑定到FetchRequest的NSPredicate
动态修改@FetchRequest的NSPredicate实现方案
问题描述
我有一个带分段选择器的视图,用来展示FetchRequest的查询结果,希望选择器值变化时,关联请求的NSPredicate也跟着更新。
尝试了以下代码,但因为@FetchRequest和@State变量初始化顺序问题,无法直接使用@State变量:
@FetchRequest(sortDescriptors: [ SortDescriptor(\.date, order: .reverse) ], predicate: NSPredicate(format: "date >= %@", selectedTimeFrame.getDate() as NSDate)) var sessions: FetchedResults<Session> @State private var selectedTimeFrame = TimeFrame.thisWeek
触发错误:
Cannot use instance member 'selectedTimeFrame' within property initializer; property initializers run before 'self' is available
解决方案
解法1:通过视图初始化方法配置@FetchRequest(兼容iOS13+)
将@FetchRequest声明为可变属性,在视图初始化时传入初始谓词,再通过onChange监听选择器值变化,动态更新请求的谓词:
struct SessionListView: View { @State private var selectedTimeFrame = TimeFrame.thisWeek @FetchRequest var sessions: FetchedResults<Session> init() { let initialPredicate = NSPredicate(format: "date >= %@", selectedTimeFrame.getDate() as NSDate) _sessions = FetchRequest( sortDescriptors: [SortDescriptor(\.date, order: .reverse)], predicate: initialPredicate ) } var body: some View { Picker("时间范围", selection: $selectedTimeFrame) { Text("本周").tag(TimeFrame.thisWeek) Text("本月").tag(TimeFrame.thisMonth) Text("全部").tag(TimeFrame.all) } .pickerStyle(.segmented) .onChange(of: selectedTimeFrame) { newTimeFrame in let newPredicate = NSPredicate(format: "date >= %@", newTimeFrame.getDate() as NSDate) _sessions = FetchRequest( sortDescriptors: [SortDescriptor(\.date, order: .reverse)], predicate: newPredicate ) } List(sessions) { session in Text(session.date.description) } } } // 示例TimeFrame枚举及日期计算逻辑 enum TimeFrame { case thisWeek, thisMonth, all func getDate() -> Date { switch self { case .thisWeek: return Calendar.current.date(byAdding: .weekOfYear, value: -1, to: Date())! case .thisMonth: return Calendar.current.date(byAdding: .month, value: -1, to: Date())! case .all: return Date.distantPast } } }
解法2:使用iOS17+的@Query属性(更简洁)
iOS17及以上版本推出的@Query属性支持直接动态修改谓词,无需手动管理FetchRequest初始化:
import SwiftData struct SessionListView: View { @State private var selectedTimeFrame = TimeFrame.thisWeek @Query(sort: \.date, order: .reverse) var sessions: [Session] var body: some View { Picker("时间范围", selection: $selectedTimeFrame) { Text("本周").tag(TimeFrame.thisWeek) Text("本月").tag(TimeFrame.thisMonth) Text("全部").tag(TimeFrame.all) } .pickerStyle(.segmented) .onChange(of: selectedTimeFrame) { _ in $sessions.predicate = NSPredicate(format: "date >= %@", selectedTimeFrame.getDate() as NSDate) } .task { // 初始设置谓词 $sessions.predicate = NSPredicate(format: "date >= %@", selectedTimeFrame.getDate() as NSDate) } List(sessions) { session in Text(session.date.description) } } }
内容的提问来源于stack exchange,提问作者Skoua
相关产品推荐
相关产品推荐

