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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 13:15:01