| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 |
- //
- // AlarmThresholdPickerSection.swift
- // LoopFollow
- //
- // Created by Jonas Björkert on 2025-05-06.
- // Copyright © 2025 Jon Fawcett. All rights reserved.
- //
- import SwiftUI
- import HealthKit
- struct AlarmThresholdRow: View {
- // ── Public API ──────────────────────────────────────────────────────────────
- let title: String
- let range: ClosedRange<Double>
- let step: Double // 1 for mg/dL, 0.1 for mmol/L
- @Binding var value: Double // **stored in mg/dL**
- // ── Private state ──────────────────────────────────────────────────────────
- @State private var showPicker = false
- // Preferred unit – mmol/L or mg/dL
- private var unit: HKUnit { UserDefaultsRepository.getPreferredUnit() }
- private var displayValue: String {
- format(value, in: unit)
- }
- // Generate all selectable display values for the picker
- private var pickerValues: [Double] {
- stride(from: range.lowerBound, through: range.upperBound, by: step)
- .map { $0 }
- }
- var body: some View {
- Section {
- // Collapsed row
- HStack {
- Text(title)
- Spacer()
- Text(displayValue)
- .foregroundColor(.secondary)
- }
- .contentShape(Rectangle())
- .onTapGesture { showPicker = true }
- }
- .sheet(isPresented: $showPicker) {
- // Expanded wheel picker
- NavigationStack {
- VStack {
- Picker("", selection: bindingForPicker) {
- ForEach(pickerValues, id: \.self) { v in
- Text(format(v, in: unit))
- .tag(v)
- }
- }
- .pickerStyle(.wheel)
- }
- .navigationTitle(title)
- .toolbar {
- ToolbarItem(placement: .confirmationAction) {
- Button("Done") { showPicker = false }
- }
- }
- } .presentationDetents([.fraction(0.35), .medium]) // 35 % or medium height
- .presentationDragIndicator(.visible) // the little grab-bar
- }
- }
- // MARK: – Helpers
- private func format(_ mgdl: Double, in unit: HKUnit) -> String {
- if unit == .millimolesPerLiter {
- let mmol = mgdl / 18.0
- return String(format: "%.1f mmol/L", mmol)
- } else {
- return String(format: "%.0f mg/dL", mgdl)
- }
- }
- // Bind the picker directly to `value`, snapping to the closest choice
- private var bindingForPicker: Binding<Double> {
- Binding(
- get: {
- // pick the closest representable value
- pickerValues.min(by: { abs($0 - value) < abs($1 - value) }) ?? value
- },
- set: { newVal in
- value = newVal // stored in mg/dL
- }
- )
- }
- }
|