AlarmThresholdPickerSection.swift 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. //
  2. // AlarmThresholdPickerSection.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. // ── Public API ──────────────────────────────────────────────────────────────
  12. let title: String
  13. let range: ClosedRange<Double>
  14. @Binding var value: Double
  15. // ── Private state ──────────────────────────────────────────────────────────
  16. @State private var showPicker = false
  17. // Preferred unit – mmol/L or mg/dL
  18. private var unit: HKUnit { UserDefaultsRepository.getPreferredUnit() }
  19. private var displayValue: String {
  20. Localizer.formatQuantity(value)
  21. }
  22. // Generate all selectable display values for the picker
  23. private var pickerValues: [Double] {
  24. let step : Double = unit == .millimolesPerLiter ? 18.0 * 0.1 : 1.0
  25. return stride(from: range.lowerBound, through: range.upperBound, by: step)
  26. .map { $0 }
  27. }
  28. var body: some View {
  29. Section {
  30. // Collapsed row
  31. HStack {
  32. Text(title)
  33. Spacer()
  34. Text(displayValue)
  35. .foregroundColor(.secondary)
  36. }
  37. .contentShape(Rectangle())
  38. .onTapGesture { showPicker = true }
  39. }
  40. .sheet(isPresented: $showPicker) {
  41. NavigationStack {
  42. VStack {
  43. Picker("", selection: bindingForPicker) {
  44. ForEach(pickerValues, id: \.self) { v in
  45. Text(Localizer.formatQuantity(v))
  46. .tag(v)
  47. }
  48. }
  49. .pickerStyle(.wheel)
  50. }
  51. .navigationTitle(title)
  52. .toolbar {
  53. ToolbarItem(placement: .confirmationAction) {
  54. Button("Done") { showPicker = false }
  55. }
  56. }
  57. }
  58. .presentationDetents([.fraction(0.35), .medium])
  59. .presentationDragIndicator(.visible)
  60. }
  61. }
  62. private var bindingForPicker: Binding<Double> {
  63. Binding(
  64. get: {
  65. // pick the closest representable value
  66. pickerValues.min(by: { abs($0 - value) < abs($1 - value) }) ?? value
  67. },
  68. set: { newVal in
  69. value = newVal // stored in mg/dL
  70. }
  71. )
  72. }
  73. }