Swift Charts Customize xAxis Label Position?

71 Views Asked by At

enter image description hereI 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)
                            }
                        }
      [![enter image description here][1]][1]
1

There are 1 best solutions below

3
Sweeper On BEST ANSWER

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 Dates, 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:

enter image description here