AlarmBGSection.swift 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. //
  2. // AlarmBGSection.swift
  3. // LoopFollow
  4. //
  5. // Created by Jonas Björkert on 2025-05-06.
  6. // Copyright © 2025 Jon Fawcett. All rights reserved.
  7. //
  8. import SwiftUI
  9. import HealthKit
  10. struct AlarmBGSection: View {
  11. let header: String?
  12. let footer: String?
  13. let title: String
  14. let range: ClosedRange<Double>
  15. @Binding var value: Double
  16. @State private var showPicker = false
  17. init(
  18. header: String? = nil,
  19. footer: String? = nil,
  20. title: String,
  21. range: ClosedRange<Double>,
  22. value: Binding<Double>
  23. ) {
  24. self.header = header
  25. self.footer = footer
  26. self.title = title
  27. self.range = range
  28. self._value = value
  29. }
  30. private var unit: HKUnit { UserDefaultsRepository.getPreferredUnit() }
  31. private var displayValue: String {
  32. let formatted = Localizer.formatQuantity(value)
  33. return "\(formatted) \(unit.localizedShortUnitString)"
  34. }
  35. private var pickerValues: [Double] {
  36. let step : Double = unit == .millimolesPerLiter ? 18.0 * 0.1 : 1.0
  37. return stride(from: range.lowerBound, through: range.upperBound, by: step)
  38. .map { $0 }
  39. }
  40. var body: some View {
  41. Section(
  42. header: header.map(Text.init),
  43. footer: footer.map(Text.init)
  44. ) {
  45. HStack {
  46. Text(title)
  47. Spacer()
  48. Text(displayValue)
  49. .foregroundColor(.secondary)
  50. }
  51. .contentShape(Rectangle())
  52. .onTapGesture { showPicker = true }
  53. }
  54. .sheet(isPresented: $showPicker) {
  55. NavigationStack {
  56. VStack {
  57. Picker("", selection: bindingForPicker) {
  58. ForEach(pickerValues, id: \.self) { v in
  59. Text("\(Localizer.formatQuantity(v)) \(unit.localizedShortUnitString)")
  60. .tag(v)
  61. }
  62. }
  63. .pickerStyle(.wheel)
  64. }
  65. .navigationTitle(title)
  66. .toolbar {
  67. ToolbarItem(placement: .confirmationAction) {
  68. Button("Done") { showPicker = false }
  69. }
  70. }
  71. }
  72. .presentationDetents([.fraction(0.35), .medium])
  73. .presentationDragIndicator(.visible)
  74. }
  75. }
  76. private var bindingForPicker: Binding<Double> {
  77. Binding(
  78. get: {
  79. // pick the closest representable value
  80. pickerValues.min(by: { abs($0 - value) < abs($1 - value) }) ?? value
  81. },
  82. set: { newVal in
  83. value = newVal // stored in mg/dL
  84. }
  85. )
  86. }
  87. }