I am trying to play around with the SwiftUI Charts framework to make a bar graph, where the x axis is a date, and the y axis is the number of seconds.
As the number of seconds grows, at some point, the data starts to loose value, as the seconds start to get too large. I was looking to convert it to the next largest date component.
As an example: 90s->1.5 min 20,000s->5.5hr
Is there a way to do this within the .ChartYAxis{} that is available
import Foundation
import SwiftUI
import Charts
public struct BarChartData: Identifiable, Equatable {
public let id = UUID()
let date: Date
let count: Double
let runningTotal: Double
init(date: Date, count: Double, runningTotal: Double) {
self.date = date
self.count = count
self.runningTotal = runningTotal
}
}
struct BarChart: View {
@Binding public var barChartData: [BarChartData]
@Binding public var selectedInterval: TimeManager.TimeIntervalOption
var body: some View {
GroupBox ("History"){
Chart {
ForEach(barChartData) { datum in
BarMark(
x: .value("Date", datum.date, unit: getGraphUnit(selectedInterval)),
y: .value("Count", datum.count)
)
.foregroundStyle(by: .value("Value", "Time"))
LineMark(
x: .value("Date", datum.date, unit: getGraphUnit(selectedInterval)),
y: .value("Total", datum.runningTotal)
)
.foregroundStyle(by: .value("Value", "Total"))
}
}
.padding()
.frame(maxWidth: .infinity, maxHeight: 300)
.chartForegroundStyleScale([
"Time": Color.purple,
"Total": Color.rewindsRed
])
.chartXAxis {
AxisMarks(position: .bottom, values: .stride(by: getGraphUnit(selectedInterval), count: getStrideByUnit(selectedInterval))) {
_ in
AxisTick()
AxisGridLine()
AxisValueLabel(format: getAxisUnit(selectedInterval), centered: false)
}
}
}
}
private func getAxisUnit(_ selectedInterval: TimeManager.TimeIntervalOption) -> Date.FormatStyle {
switch selectedInterval {
case .Day:
return .dateTime.hour(.twoDigits(amPM: .abbreviated))
case .Week:
return .dateTime.weekday(.abbreviated)
case .Month:
return .dateTime.day(.defaultDigits)
case .Year:
return .dateTime.month(.abbreviated)
case .Lifetime:
return .dateTime.year(.extended(minimumLength: 4))
}
}
private func getGraphUnit(_ selectedInterval: TimeManager.TimeIntervalOption) -> Calendar.Component {
switch selectedInterval {
case .Day:
return .hour
case .Week:
return .day
case .Month:
return .day
case .Year:
return .month
case .Lifetime:
return .year
}
}
private func getStrideByUnit(_ selectedInterval: TimeManager.TimeIntervalOption) -> Int {
switch selectedInterval {
case .Day:
return 4
case .Week:
return 1
case .Month:
return 7
case .Year:
return 12
case .Lifetime:
return 1
}
}
}
struct BarChart_Previews: PreviewProvider {
static let testData: [BarChartData] = [
BarChartData(date: Date(dateString: "2023-07-18 16:00:00 +0000", format: "yyyy-MM-dd HH:mm:ss Z"), count: 200.0, runningTotal: 200),
BarChartData(date: Date(dateString: "2023-07-18 17:00:00 +0000", format: "yyyy-MM-dd HH:mm:ss Z"), count: 300.0, runningTotal: 500),
BarChartData(date: Date(dateString: "2023-07-18 18:00:00 +0000", format: "yyyy-MM-dd HH:mm:ss Z"), count: 200.0, runningTotal: 700),
BarChartData(date: Date(dateString: "2023-07-18 19:00:00 +0000", format: "yyyy-MM-dd HH:mm:ss Z"), count: 200.0, runningTotal: 900),
BarChartData(date: Date(dateString: "2023-07-18 20:00:00 +0000", format: "yyyy-MM-dd HH:mm:ss Z"), count: 200.0, runningTotal: 1100),
BarChartData(date: Date(dateString: "2023-07-18 21:00:00 +0000", format: "yyyy-MM-dd HH:mm:ss Z"), count: 200.0, runningTotal: 1300),
BarChartData(date: Date(dateString: "2023-07-18 22:00:00 +0000", format: "yyyy-MM-dd HH:mm:ss Z"), count: 200.0, runningTotal: 1500),
]
static var previews: some View {
BarChart(barChartData: .constant(testData), selectedInterval: .constant(.Day))
}
}
I explored trying to use the ChartAxisLabel, as a way to convert the value, as it has a format method of .timeDuration, that I found, but it was unavailable for use. I’m