LabeledNumberInput.swift 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. //
  2. // LabeledNumberInput.swift
  3. // LoopKitUI
  4. //
  5. // Created by Nathaniel Hamming on 2020-02-20.
  6. // Copyright © 2020 LoopKit Authors. All rights reserved.
  7. //
  8. import SwiftUI
  9. public struct LabeledNumberInput: View {
  10. @Binding var value: Double?
  11. var label: String
  12. var placeholder: String
  13. var allowFractions: Bool
  14. private var numberFormatter: NumberFormatter {
  15. let numberFormatter = NumberFormatter()
  16. numberFormatter.numberStyle = allowFractions ? .decimal : .none
  17. return numberFormatter
  18. }
  19. // seems like the TextField doesn't update the formatted binding until return to tapped. This is the workaround.
  20. private var valueString: Binding<String> {
  21. Binding<String>(
  22. get: { () -> String in
  23. guard let value = self.value else {
  24. return ""
  25. }
  26. return self.numberFormatter.string(from: NSNumber(value: value.rawValue)) ?? ""
  27. },
  28. set: {
  29. if let value = self.numberFormatter.number(from: $0) {
  30. self.value = value.doubleValue
  31. }
  32. }
  33. )
  34. }
  35. public init(value: Binding<Double?>, label: String, placeholder: String? = nil, allowFractions: Bool = false) {
  36. _value = value
  37. self.label = label
  38. self.placeholder = placeholder ?? LocalizedString("Value", comment: "Placeholder text until value is entered")
  39. self.allowFractions = allowFractions
  40. }
  41. public var body: some View {
  42. GeometryReader { geometry in
  43. HStack(alignment: .bottom, spacing: 5) {
  44. TextField(self.placeholder, text: self.valueString)
  45. .font(.largeTitle)
  46. .multilineTextAlignment(.trailing)
  47. .keyboardType(self.allowFractions ? .decimalPad : .numberPad)
  48. .frame(width: geometry.size.width/2, alignment: .trailing)
  49. .accessibility(label: Text(String(format: LocalizedString("Enter %1$@ value", comment: "Format string for accessibility label for value entry. (1: value label)"), label)))
  50. Text(self.label)
  51. .font(.footnote)
  52. .multilineTextAlignment(.leading)
  53. .foregroundColor(.secondary)
  54. .padding(.bottom, 7)
  55. .frame(width: geometry.size.width/2, alignment: .leading)
  56. }
  57. }
  58. }
  59. }
  60. struct LabeledNumberInput_Previews: PreviewProvider {
  61. static var previews: some View {
  62. LabeledNumberInput(
  63. value: .constant(nil),
  64. label: "mg/dL",
  65. allowFractions: true)
  66. }
  67. }