SwiftUI:如何编写Fetch Request获取每日最大重量数据?
解决方案:按日期获取每日最大重量记录
要实现按日期取每日最大重量的需求,你有两种可行方案,根据数据量和性能需求选择即可:
方案一:内存中分组过滤(简单易实现,适合数据量不大的场景)
直接利用现有@FetchRequest获取所有相关记录,然后在内存中按日期分组,每组筛选出重量最大的那条记录,最后按日期排序用于图表绘制。
修改代码步骤:
- 在
ExerciseDetailView中添加一个计算属性,处理原始数据:
// 处理后的数据:每个日期仅保留最大重量的记录 var dailyMaxSets: [ExerciseSet] { // 按当日起始时间分组 let groupedByDate = Dictionary(grouping: exercisesets) { set in set.dateCompleted.startOfDay } // 每组取重量最大的记录,再按日期升序排序 return groupedByDate.values.compactMap { setsInDay in setsInDay.max(by: { $0.weight < $1.weight }) }.sorted { $0.dateCompleted < $1.dateCompleted } }
- 修改图表数据源,同时修正Y轴绑定的属性(你原代码中写的是
reps,需求是展示重量,所以改成weight):
Chart(dailyMaxSets) { e in LineMark(x: .value("日期", e.dateCompleted.startOfDay), y: .value("最大重量", e.weight) ) }
- 确保你的
Date扩展包含startOfDay方法(如果没有的话添加):
extension Date { var startOfDay: Date { Calendar.current.startOfDay(for: self) } }
方案二:Core Data聚合查询(性能更优,适合数据量大的场景)
如果你的运动记录数据量很大,内存处理会有性能损耗,可以直接通过Core Data的分组聚合查询,只获取每个日期的最大重量数据,减少数据传输量。
修改代码步骤:
- 创建一个简单模型存储聚合结果:
struct DailyMaxWeight: Identifiable { let id: Date let date: Date let maxWeight: Double }
- 实现数据管理类,用于执行聚合查询:
class ExerciseDataManager: ObservableObject { @Published var dailyMaxWeights: [DailyMaxWeight] = [] let exerciseName: String init(exerciseName: String) { self.exerciseName = exerciseName fetchDailyMaxWeights() } private func fetchDailyMaxWeights() { let request = NSFetchRequest<NSDictionary>(entityName: "ExerciseSet") // 过滤指定运动的记录 request.predicate = NSPredicate(format: "exercisename == %@", exerciseName) // 按日期升序排序 request.sortDescriptors = [NSSortDescriptor(key: "dateCompleted", ascending: true)] // 按当日起始时间分组 request.propertiesToGroupBy = ["dateCompleted.startOfDay"] // 定义聚合表达式:获取每组的最大重量 let maxWeightExpression = NSExpressionDescription() maxWeightExpression.name = "maxWeight" maxWeightExpression.expression = NSExpression(forFunction: "max:", arguments: [NSExpression(forKeyPath: "weight")]) maxWeightExpression.expressionResultType = .doubleAttributeType // 指定要获取的字段 request.propertiesToFetch = ["dateCompleted.startOfDay", maxWeightExpression] // 设置结果类型为字典 request.resultType = .dictionaryResultType do { // 替换成你项目中实际的Core Data上下文 let results = try PersistenceController.shared.container.viewContext.fetch(request) dailyMaxWeights = results.compactMap { dict in guard let date = dict["dateCompleted.startOfDay"] as? Date, let maxWeight = dict["maxWeight"] as? Double else { return nil } return DailyMaxWeight(id: date, date: date, maxWeight: maxWeight) } } catch { print("获取每日最大重量失败:\(error)") } } }
- 修改
ExerciseDetailView使用数据管理类:
struct ExerciseDetailView: View { @StateObject private var dataManager: ExerciseDataManager var exercise: String init(exercise: String) { self.exercise = exercise self._dataManager = StateObject(wrappedValue: ExerciseDataManager(exerciseName: exercise)) } var body: some View { NavigationView { ScrollView { if #available(iOS 16.0, *) { Chart(dataManager.dailyMaxWeights) { item in LineMark(x: .value("日期", item.date), y: .value("最大重量", item.maxWeight) ) } } else { Text("需要iOS 16及以上版本才能查看图表") } } .navigationTitle(exercise) } } }
内容的提问来源于stack exchange,提问作者Rachel
相关产品推荐
相关产品推荐

