Swift Chart Y轴排序问题:睡眠阶段轴序调整求助
解决方案:通过整数映射自定义Y轴顺序
问题根源在于你使用了String类型作为Y轴值,Swift Charts会默认按字典序排序字符串,导致顺序不符合需求。用整数映射睡眠阶段可以完全自定义排序,同时保证每个阶段占相同高度,步骤如下:
1. 修改SleepType枚举,添加排序用的整数映射
给枚举增加一个计算属性,返回对应排序的整数(顶部值最大,底部值最小):
enum SleepType: String, CaseIterable { case Awake, REM, Core, Deep // 自定义排序:Awake(顶部) → REM → Core → Deep(底部) var orderValue: Int { switch self { case .Awake: return 3 case .REM: return 2 case .Core: return 1 case .Deep: return 0 } } }
2. 调整Chart的Y轴值为整数映射
在LineMark中,将Y轴的值替换为orderValue(整数类型):
Chart { ForEach(sleep, id: \.self) { sleep in LineMark( x: .value("Time", sleep.startDate), y: .value("Type", sleep.sleepType.orderValue) ) } }
3. 配置Y轴,将整数映射回睡眠阶段名称
自定义chartYAxis,指定整数刻度,并将每个刻度转换为对应的睡眠阶段名称:
.chartYAxis { AxisMarks(values: [3, 2, 1, 0]) { value in guard let intValue = value.as(Int.self), let sleepType = SleepType.allCases.first(where: { $0.orderValue == intValue }) else { return } AxisGridLine() // 显示网格线 AxisTick() // 显示刻度线 AxisValueLabel(sleepType.rawValue) // 显示睡眠阶段名称 } }
完整修改后的代码
enum SleepType: String, CaseIterable { case Awake, REM, Core, Deep var orderValue: Int { switch self { case .Awake: return 3 case .REM: return 2 case .Core: return 1 case .Deep: return 0 } } } struct SingleSleep: Hashable, Identifiable { let id = UUID() let startDate: Date let stopDate: Date var sleepTime: TimeInterval = 0 //in minutes var sleepType: SleepType } struct SleepExampleView: View { var sleep = [ SingleSleep(startDate: Date(timeIntervalSinceReferenceDate: 729208384), stopDate: Date(timeIntervalSinceReferenceDate: 729209374), sleepTime: 16.5, sleepType: .Core), SingleSleep(startDate: Date(timeIntervalSinceReferenceDate: 729209374), stopDate: Date(timeIntervalSinceReferenceDate: 729209404), sleepTime: 16.5, sleepType: .Awake), SingleSleep(startDate: Date(timeIntervalSinceReferenceDate: 729209404), stopDate: Date(timeIntervalSinceReferenceDate: 729209794), sleepTime: 16.5, sleepType: .Core), SingleSleep(startDate: Date(timeIntervalSinceReferenceDate: 729209794), stopDate: Date(timeIntervalSinceReferenceDate: 729211234), sleepTime: 16.5, sleepType: .Deep), SingleSleep(startDate: Date(timeIntervalSinceReferenceDate: 729211234), stopDate: Date(timeIntervalSinceReferenceDate: 729211864), sleepTime: 16.5, sleepType: .REM), ] var body: some View { Chart { ForEach(sleep, id: \.self) { sleep in LineMark( x: .value("Time", sleep.startDate), y: .value("Type", sleep.sleepType.orderValue) ) } } .chartYAxis { AxisMarks(values: [3, 2, 1, 0]) { value in guard let intValue = value.as(Int.self), let sleepType = SleepType.allCases.first(where: { $0.orderValue == intValue }) else { return } AxisGridLine() AxisTick() AxisValueLabel(sleepType.rawValue) } } .chartXAxis { AxisMarks(values: .stride(by: .hour, count: 1)) { value in AxisValueLabel(format: .dateTime.hour(), centered: true) } } .frame(width: 300, height: 300) .preferredColorScheme(.dark) .background(.clear) } }
额外优化建议
如果要更直观地展示睡眠阶段(时间段块),可以将LineMark替换为RectangleMark,并设置X轴的范围为startDate到stopDate:
RectangleMark( xStart: .value("Start Time", sleep.startDate), xEnd: .value("End Time", sleep.stopDate), y: .value("Type", sleep.sleepType.orderValue), height: .fixed(60) // 固定高度,或者用.ratio(0.25)占25% )
内容的提问来源于stack exchange,提问作者joeymichigan
相关产品推荐
相关产品推荐

