that tracks someone's sleep into the 4 categories awake, rem, core and deep. I have the graph almost how I want it, showing the sleep data at various times of the night (each hour), however I am lost on how I am meant to get my Y axis showing in the right order. I want it split into four, for each of the sleep stages, starting top awake, below REM, below core, at the bottom deep, 25% of the height of the graph. However, no matter what I try, it maintains the order core, awake, deep, REM. I have tried using the .chartYAxis, but it didnt seem to do anything. I can see in the documentation how to adjust this for numbers, or dates, but for what I want, I am pretty lost, especially as I get no errors, it just fails silently to order them. I thought about somehow using an Integer to represent each of the sleep types, 0 = awake, 4 = deep, and somehow correspond those to a String value, but I am not sure how to achieve that. Thanks for the help!
enum SleepType: String, Plottable, CaseIterable {
case Awake
case REM
case Core
case Deep
}
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
Chart {
ForEach(sleep, id: \.self) { sleep in
LineMark(
x: .value("Time", sleep.startDate),
y: .value("Type", sleep.sleepType.rawValue)
)
}
}
.chartYAxis {
AxisMarks(
values: [SleepType.Awake.rawValue, SleepType.REM.rawValue, SleepType.Core.rawValue, SleepType.Deep.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)
}
}
The order of the y values is a problem about the domain, not the axis.
You can use chartYScale
to modify the domain to whatever set of values you want:
Chart {
ForEach(sleep) { sleep in
LineMark(
x: .value("Time", sleep.startDate),
y: .value("Type", sleep.sleepType)
)
}
}
.chartYScale(domain: .automatic(dataType: SleepType.self, modifyInferredDomain: { domain in
// put your order here:
domain = [SleepType.Awake, .REM, .Core, .Deep]
}))
// you don't need chartYAxis here since sleep type is discrete data
// the y axis shows all the values by default
.chartXAxis {
AxisMarks(values: .stride(by: .hour, count: 1)) { value in
AxisValueLabel(format: .dateTime.hour(), centered: true)
}
}
Note that I used the SleepType
values themselves to plot the y axis, instead of their rawValue
s. SleepType
conforms to Plottable
after all.