Skip to content

Instantly share code, notes, and snippets.

@LucaKaufmann
Last active October 4, 2023 18:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LucaKaufmann/a0dc71a7dd365715932160a9841e8e8d to your computer and use it in GitHub Desktop.
Save LucaKaufmann/a0dc71a7dd365715932160a9841e8e8d to your computer and use it in GitHub Desktop.
SwiftUI Scrollable chart issue
import SwiftUI
import Charts
public enum StatisticsTimeFrame: String, CaseIterable {
case week = "Week"
case month = "Month"
case year = "Year"
}
public struct SleepStatisticDatapoint: Equatable {
let date: Date
let interval: TimeInterval
static var mockData: [SleepStatisticDatapoint] = {
let calendar = Calendar.current
var datapoints: [SleepStatisticDatapoint] = []
for i in 0..<30 {
let date = calendar.date(byAdding: .day, value: -i, to: Date.now)!
let interval = TimeInterval.random(in: 0...86400)
datapoints.append(SleepStatisticDatapoint(date: date, interval: interval))
}
return datapoints
}()
}
struct ContentView: View {
let chartAxisColor = Color.red
@State var formatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute]
formatter.unitsStyle = .abbreviated
formatter.zeroFormattingBehavior = .pad
return formatter
}()
@State var datapoints: [SleepStatisticDatapoint] = SleepStatisticDatapoint.mockData
@State var timeframe: StatisticsTimeFrame = .week
public var body: some View {
VStack {
HStack {
VStack(alignment: .leading) {
Text("USUAL BEDTIME")
.font(.caption)
Text("...")
.fontWeight(.semibold)
}
Spacer()
}
.foregroundColor(Color.blue)
.padding()
Chart(datapoints, id: \.date) {
BarMark(
x: .value("date", $0.date, unit: componentForTimeframe(timeframe)),
y: .value("duration", $0.interval)
)
.accessibilityLabel($0.date.formatted(date: .complete, time: .omitted))
.accessibilityValue("\($0.interval) sleep")
.foregroundStyle(Color.green)
}
.chartScrollableAxes(.horizontal)
// This part causes issues
.chartXVisibleDomain(length: 7)
.chartXScale(domain: startDateForTimeFrame(timeframe)...Date.now)
.chartXAxis {
AxisMarks(values: axisMarkValuesForTimeframe(timeframe)) { value in
AxisValueLabel(format: formatStyleForTimeframe(timeframe))
.foregroundStyle(chartAxisColor)
AxisTick()
.foregroundStyle(chartAxisColor)
}
}
.chartYAxis {
AxisMarks(values: .stride(by: 14400)) { value in
AxisValueLabel {
Text("")
.foregroundStyle(chartAxisColor)
}
}
}
}
Picker("Chart timeframe", selection: $timeframe) {
ForEach(StatisticsTimeFrame.allCases, id: \.self) { timeframe in
Text(timeframe.rawValue)
}
}
.pickerStyle(.segmented)
.padding()
}
private func startDateForTimeFrame(_ timeframe: StatisticsTimeFrame) -> Date {
let calendar = Calendar.current
let date: Date
switch timeframe {
case .week:
date = calendar.date(byAdding: .day, value: -6, to: Date.now)!
case .month:
date = calendar.date(byAdding: .month, value: -1, to: Date.now)!
case .year:
date = calendar.date(byAdding: .year, value: -1, to: Date.now)!
}
return date
}
private func componentForTimeframe(_ timeframe: StatisticsTimeFrame) -> Calendar.Component {
let component: Calendar.Component
switch timeframe {
case .week:
component = .weekday
case .month:
component = .day
case .year:
component = .month
}
return component
}
private func formatStyleForTimeframe(_ timeframe: StatisticsTimeFrame) -> Date.FormatStyle {
let style: Date.FormatStyle
switch timeframe {
case .week:
style = .dateTime.weekday()
case .month:
style = .dateTime.day()
case .year:
style = .dateTime.month()
}
return style
}
private func axisMarkValuesForTimeframe(_ timeframe: StatisticsTimeFrame) -> AxisMarkValues {
let values: AxisMarkValues
switch timeframe {
case .week:
values = .stride(by: .day, count: 1)
case .month:
values = .automatic
case .year:
values = .stride(by: .month, count: 1)
}
return values
}
}
#Preview {
ContentView()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment