AlarmStepperSection.swift 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. // LoopFollow
  2. // AlarmStepperSection.swift
  3. // Created by Jonas Björkert on 2025-05-10.
  4. import SwiftUI
  5. struct AlarmStepperSection: View {
  6. // MARK: – public parameters
  7. let header: String?
  8. let footer: String?
  9. let title: String
  10. let range: ClosedRange<Double>
  11. let step: Double
  12. let unitLabel: String?
  13. // MARK: – private binding (always Double?)
  14. @Binding
  15. private var value: Double?
  16. // MARK: – designated initialiser (Double?)
  17. init(
  18. header: String? = nil,
  19. footer: String? = nil,
  20. title: String,
  21. range: ClosedRange<Double>,
  22. step: Double,
  23. unitLabel: String? = nil,
  24. value: Binding<Double?>
  25. ) {
  26. self.header = header
  27. self.footer = footer
  28. self.title = title
  29. self.range = range
  30. self.step = step
  31. self.unitLabel = unitLabel
  32. _value = value
  33. }
  34. // MARK: – convenience initialiser (Int?)
  35. /// Same API but for **`Binding<Int?>`** — it bridges to Double internally.
  36. init(
  37. header: String? = nil,
  38. footer: String? = nil,
  39. title: String,
  40. range: ClosedRange<Double>,
  41. step: Double,
  42. unitLabel: String? = nil,
  43. value intValue: Binding<Int?>
  44. ) {
  45. self.init(
  46. header: header,
  47. footer: footer,
  48. title: title,
  49. range: range,
  50. step: step,
  51. unitLabel: unitLabel,
  52. value: Binding<Double?>(
  53. get: { intValue.wrappedValue.map(Double.init) },
  54. set: { newVal in intValue.wrappedValue = newVal.map(Int.init) }
  55. )
  56. )
  57. }
  58. // MARK: – derived non-optional Binding<Double>
  59. private var nonOptional: Binding<Double> {
  60. Binding(
  61. get: { value ?? range.lowerBound },
  62. set: { newVal in value = newVal }
  63. )
  64. }
  65. // MARK: – view
  66. var body: some View {
  67. Section(
  68. header: header.map(Text.init),
  69. footer: footer.map(Text.init)
  70. ) {
  71. Stepper(value: nonOptional, in: range, step: step) {
  72. HStack {
  73. Text(title)
  74. Spacer()
  75. Text(
  76. "\(Int(nonOptional.wrappedValue))" +
  77. (unitLabel.map { " \($0)" } ?? "")
  78. )
  79. .foregroundColor(.secondary)
  80. }
  81. }
  82. }
  83. }
  84. }