There are totaly 2 questions
When using SwiftUI's Chart with BarMark for plotting data with Date values on the Y-axis, the issue arises where the time displayed on the Y-axis appears to be inverted—i.e., the smaller times (earlier hours) are shown at the top, while the larger times (later hours) are displayed at the bottom.
There is another question, I don't know why this code always reports The compiler is unable to type check this expression in reasonable time
; try breaking up the expression into distinct sub-expressions
The second issue is caused by extracting this section of the code for display from the original code.
import SwiftUI
import Charts
// Helper function to extract the hour and minute from a Date
func extractHourMinuteFromDate(_ date: Date) -> Date {
let calendar = Calendar.current
let components = calendar.dateComponents([.hour, .minute], from: date)
return calendar.date(from: components) ?? date
}
// Helper function to convert a time string to TimeInterval (if needed)
func timeStringToTimeInterval(_ timeString: String) -> TimeInterval? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm:ss"
if let date = dateFormatter.date(from: timeString) {
return date.timeIntervalSinceReferenceDate
}
return nil
}
struct HomeHistogramView: View {
var viewData: HistogramViewData
@State private var selectedWeek: Int = 0
var body: some View {
let weeklyData=viewData.getWeeklyData()
TabView(selection: $selectedWeek) {
ForEach(0..<weeklyData.count, id: \.self) { weekIndex in
VStack {
Chart(weeklyData[weekIndex]) { item in
BarMark(
x: .value("Date", item.date),
yStart: .value("Start", item.start),
yEnd: .value("End", item.end),
width: 30
)
.foregroundStyle(Color(viewData.chartColor))
.cornerRadius(4)
}
.chartYAxis {
AxisMarks(position: .leading) {
AxisGridLine()
AxisTick()
AxisValueLabel(format: .dateTime.hour(.twoDigits(amPM: .omitted)).minute(.twoDigits))
}
}
.frame(height: 130)
}
.tag(weekIndex)
}
.tabViewStyle(PageTabViewStyle())
.frame(height: 150)
.onAppear {
// Default to the last week's data
selectedWeek = weeklyData.count - 1
}
}
.padding(16)
}
}
// Function to create a Date object from hour and minute
func dateFrom(hour: Int, minute: Int) -> Date {
let calendar = Calendar.current
var components = DateComponents()
components.hour = hour
components.minute = minute
return calendar.date(from: components) ?? Date()
}
struct myData: Identifiable, Codable {
var id = UUID()
var date: String
var detailDate: String
var start: Date
var end: Date
}
struct HistogramViewData: Identifiable, Codable {
var id = UUID()
var time: String
var isArtificial: Bool
var dataArray: [myData] = []
// Method to get weekly data
func getWeeklyData() -> [[myData]] {
var twoDimensionalArray: [[myData]] = []
var subarray: [myData] = []
for element in self.dataArray {
subarray.append(element)
if subarray.count == 7 {
twoDimensionalArray.append(subarray)
subarray = []
}
}
if !subarray.isEmpty {
twoDimensionalArray.append(subarray)
}
return twoDimensionalArray
}
}
struct MainChartsView_Previews: PreviewProvider {
static var previews: some View {
let sampleSleepData: [myData] = [
myData(date: "9", detailDate: "9", start: extractHourMinuteFromDate(dateFrom(hour: 6, minute: 0)), end: extractHourMinuteFromDate(dateFrom(hour: 22, minute: 0))),
myData(date: "10", detailDate: "9", start: extractHourMinuteFromDate(dateFrom(hour: 6, minute: 0)), end: extractHourMinuteFromDate(dateFrom(hour: 23, minute: 0))),
myData(date: "11", detailDate: "9", start: extractHourMinuteFromDate(dateFrom(hour: 6, minute: 30)), end: extractHourMinuteFromDate(dateFrom(hour: 21, minute: 30))),
myData(date: "12", detailDate: "9", start: extractHourMinuteFromDate(dateFrom(hour: 0, minute: 0)), end: extractHourMinuteFromDate(dateFrom(hour: 8, minute: 0))),
myData(date: "13", detailDate: "9", start: extractHourMinuteFromDate(dateFrom(hour: 6, minute: 30)), end: extractHourMinuteFromDate(dateFrom(hour: 7, minute: 30)))
]
let viewData = HistogramViewData(
time: "8:42 ",
isArtificial: true,
dataArray: sampleSleepData
)
HomeHistogramView(viewData: viewData)
}
}
I have explored different approaches but none of them have successfully addressed the problem. I need a solution that ensures the time values are displayed correctly, with earlier times at the bottom and later times at the top, as expected.
It would be greatly appreciated if someone could explain this.
The default behaviour for displaying dates on the Y axis is to show earlier dates on the top. This makes sense because the natural reading direction is from top to bottom. If your graph is showing a time series of events, it is natural for people to start at the top and read downwards.
You can manually reverse the axis by using
.chartYScale(domain: .automatic(reversed: true))