SensorLifecycleArcView.swift 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. import LoopKit
  2. import LoopKitUI
  3. import SwiftUI
  4. /// Concentric outer ring around the glucose bobble that drains clockwise from
  5. /// 12 o'clock as the sensor session ages. Always renders a faint track behind
  6. /// the fill so the indicator's footprint is visible even at `progress == 0`.
  7. struct SensorLifecycleArcView: View {
  8. let progress: Double
  9. let progressState: DeviceLifecycleProgressState
  10. /// Outer diameter — bobble is ~130pt; arc sits 10pt outside the ring stroke.
  11. static let diameter: CGFloat = 146
  12. static let strokeWidth: CGFloat = 4
  13. private var arcColor: Color {
  14. switch progressState {
  15. case .critical:
  16. return Color.loopRed
  17. case .warning:
  18. return Color.orange
  19. case .dimmed,
  20. .normalCGM,
  21. .normalPump:
  22. return Color.loopGreen
  23. }
  24. }
  25. var body: some View {
  26. ZStack {
  27. Circle()
  28. .stroke(Color.secondary.opacity(0.4), lineWidth: Self.strokeWidth)
  29. Circle()
  30. .trim(from: 0, to: max(0, min(1, progress)))
  31. .stroke(arcColor, style: StrokeStyle(lineWidth: Self.strokeWidth, lineCap: .round))
  32. .rotationEffect(.degrees(-90))
  33. .animation(.easeInOut(duration: 0.25), value: progress)
  34. }
  35. .frame(width: Self.diameter, height: Self.diameter)
  36. .allowsHitTesting(false)
  37. }
  38. }
  39. #Preview("Arc — progress states") {
  40. VStack(spacing: 24) {
  41. SensorLifecycleArcView(progress: 0.15, progressState: .normalCGM)
  42. SensorLifecycleArcView(progress: 0.55, progressState: .normalCGM)
  43. SensorLifecycleArcView(progress: 0.92, progressState: .warning)
  44. SensorLifecycleArcView(progress: 1.0, progressState: .critical)
  45. }
  46. .padding(40)
  47. .background(Color.black)
  48. .preferredColorScheme(.dark)
  49. }