I can't seem to find a way to control where the xAxis labels in a Swift Chart are positioned. By default this code results in 2 xAxis labels in the center of the chart (as pictured). I would like to customize this chart to show the date of the first sample on the far left and the date of the last sample on the far right. How can I accomplish this?
Chart {
ForEach(buildChartRangedBarChartObjectsFrom(workoutStartDate:
workoutDetailViewModel.fullyLoadedWorkout?.startDate ?? Date(), workoutEndDate:
workoutDetailViewModel.fullyLoadedWorkout?.endDate ?? Date(),
customCyclingCadenceObjects: unwrappedCyclingCadenceObjects)) {
chartRangedBarChartObject in
BarMark(
x: .value("Date", chartRangedBarChartObject.periodStartDate),
yStart: .value("Minimum Cadence During Minute",
chartRangedBarChartObject.minValue),
yEnd: .value("Maximum Cadence During Minute",
chartRangedBarChartObject.maxValue),
width: MarkDimension(integerLiteral: 5)
)
.foregroundStyle(Color.teal.gradient)
}
}
First, since you are plotting data of each minute, you should add the unit: .minute
parameter when creating your PlottableValue
for the x axis. This makes it much easier for the system to automatically determine (aka "guess") which axis marks you want.
You can add the axis value labels like this:
.chartXAxis {
AxisMarks(values: .automatic(desiredCount: 2)) { value in
AxisValueLabel(anchor: value.index == 1 ? .topTrailing : .topLeading)
}
}
Since you want a label on each side, the labels will have different alignments relative to the value they are associated with. The left label is "left-aligned" and the right label is "right-aligned".
.automatic(desiredCount: 2)
should always give you the values you want, but if you want to be extra sure, you can always just pass in an array of 2 Date
s, extracted from your data.
Here is a complete example, which passes the labels you want directly as an array
struct Sample: Identifiable, Hashable {
let start: Double
let height: Double
let time: Date
var id: Date { time }
}
let data = (0..<30).map {
Sample(start: .random(in: 10...20), height: .random(in: 10...16), time: Date().addingTimeInterval(Double($0) * 60))
}
struct ContentView: View {
var body: some View {
Chart(data) { sample in
BarMark(
x: .value("Time", sample.time, unit: .minute),
yStart: .value("Min", sample.start),
yEnd: .value("Max", sample.start + sample.height),
width: 5
)
}
.chartXAxis {
AxisMarks(values: [data.first!.time, data.last!.time]) { value in
AxisValueLabel(
format: .dateTime.hour().minute(),
anchor: value.index == 1 ? .topTrailing : .topLeading
)
}
}
.frame(width: 300, height: 200)
}
}
Output: