ContentView.swift 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import Charts
  2. import SwiftUI
  3. struct ContentView: View {
  4. @State private var state = WatchState()
  5. @State private var showingCarbsSheet = false
  6. @State private var showingBolusSheet = false
  7. @State private var currentPage: Double = 0
  8. @State private var rotationDegrees: Double = 0.0
  9. var body: some View {
  10. TabView(selection: $currentPage) {
  11. // Page 1: Current glucose and trend
  12. ZStack {
  13. TrendShape(rotationDegrees: rotationDegrees)
  14. .animation(.spring(response: 0.5, dampingFraction: 0.6), value: rotationDegrees)
  15. VStack(alignment: .center) {
  16. Text(state.currentGlucose)
  17. .fontWeight(.bold)
  18. .font(.system(size: 40))
  19. if let delta = state.delta {
  20. Text(delta)
  21. .font(.caption2)
  22. .foregroundStyle(.secondary)
  23. }
  24. }
  25. }
  26. .tag(0.0)
  27. .onChange(of: state.trend) { newTrend in
  28. withAnimation {
  29. updateRotation(for: newTrend)
  30. }
  31. }
  32. .toolbar {
  33. ToolbarItem(placement: .bottomBar) {
  34. Button {
  35. showingCarbsSheet = true
  36. } label: {
  37. Image(systemName: "fork.knife")
  38. }
  39. }
  40. ToolbarItem(placement: .bottomBar) {
  41. Button {
  42. showingBolusSheet = true
  43. } label: {
  44. Image(systemName: "drop.fill")
  45. }
  46. }
  47. }
  48. // Page 2: Glucose chart
  49. GlucoseChartView(glucoseValues: state.glucoseValues)
  50. .tag(1.0)
  51. }
  52. .tabViewStyle(.verticalPage)
  53. .navigationBarHidden(true)
  54. .digitalCrownRotation($currentPage, from: 0, through: 1, by: 1)
  55. .sheet(isPresented: $showingCarbsSheet) {
  56. CarbsInputView(state: state)
  57. }
  58. .sheet(isPresented: $showingBolusSheet) {
  59. BolusInputView(state: state)
  60. }
  61. }
  62. private func updateRotation(for trend: String?) {
  63. switch trend {
  64. case "↑",
  65. "↑↑": // DoubleUp, SingleUp
  66. rotationDegrees = -90
  67. case "↗": // FortyFiveUp
  68. rotationDegrees = -45
  69. case "→": // Flat
  70. rotationDegrees = 0
  71. case "↘": // FortyFiveDown
  72. rotationDegrees = 45
  73. case "↓",
  74. "↓↓": // SingleDown, DoubleDown
  75. rotationDegrees = 90
  76. default:
  77. rotationDegrees = 0
  78. }
  79. }
  80. }
  81. struct GlucoseChartView: View {
  82. let glucoseValues: [(date: Date, glucose: Double)]
  83. @State private var timeWindow: TimeWindow = .threeHours
  84. enum TimeWindow: Int {
  85. case threeHours = 3
  86. case sixHours = 6
  87. case twelveHours = 12
  88. case twentyFourHours = 24
  89. var next: TimeWindow {
  90. switch self {
  91. case .threeHours: return .sixHours
  92. case .sixHours: return .twelveHours
  93. case .twelveHours: return .twentyFourHours
  94. case .twentyFourHours: return .threeHours
  95. }
  96. }
  97. }
  98. private var filteredValues: [(date: Date, glucose: Double)] {
  99. let cutoffDate = Date().addingTimeInterval(-Double(timeWindow.rawValue) * 3600)
  100. return glucoseValues.filter { $0.date > cutoffDate }
  101. }
  102. private func glucoseColor(_ value: Double) -> Color {
  103. if value > 180 {
  104. return .orange
  105. } else if value < 70 {
  106. return .red
  107. } else {
  108. return .green
  109. }
  110. }
  111. var body: some View {
  112. Chart {
  113. ForEach(filteredValues, id: \.date) { reading in
  114. LineMark(
  115. x: .value("Time", reading.date),
  116. y: .value("Glucose", reading.glucose)
  117. )
  118. .foregroundStyle(Color.accentColor.gradient)
  119. .lineStyle(StrokeStyle(lineWidth: 2))
  120. PointMark(
  121. x: .value("Time", reading.date),
  122. y: .value("Glucose", reading.glucose)
  123. )
  124. .foregroundStyle(glucoseColor(reading.glucose))
  125. .symbolSize(40) // Kleinere Punkte
  126. }
  127. }
  128. .chartXAxis {
  129. AxisMarks(values: .automatic(desiredCount: 4)) { _ in
  130. AxisValueLabel(format: .dateTime.hour())
  131. }
  132. }
  133. .chartYAxis {
  134. AxisMarks(position: .leading)
  135. }
  136. .padding()
  137. .onTapGesture {
  138. withAnimation {
  139. timeWindow = timeWindow.next
  140. }
  141. }
  142. .overlay(alignment: .topLeading) {
  143. Text("\(timeWindow.rawValue)h")
  144. .font(.caption2)
  145. .foregroundStyle(.secondary)
  146. .padding(.leading)
  147. }
  148. }
  149. }
  150. // Rest der View-Komponenten bleiben unverändert...