Kaynağa Gözat

Refactor notes Field as well

polscm32 aka Marvout 1 yıl önce
ebeveyn
işleme
49bc151a36

+ 21 - 8
FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift

@@ -42,21 +42,34 @@ extension AddCarbs {
                     HStack {
                         Text("Carbs").fontWeight(.semibold)
                         Spacer()
-                        TextFieldWithToolBar(text: $state.carbs, placeholder: "0", shouldBecomeFirstResponder: true, numberFormatter: formatter)
+                        TextFieldWithToolBar(
+                            text: $state.carbs,
+                            placeholder: "0",
+                            shouldBecomeFirstResponder: true,
+                            numberFormatter: formatter
+                        )
                         Text(state.carbs > state.maxCarbs ? "⚠️" : "g").foregroundColor(.secondary)
                     }.padding(.vertical)
 
                     if state.useFPUconversion {
                         proteinAndFat()
                     }
-                    HStack {
-                        Text("Note").foregroundColor(.secondary)
-                        TextField("", text: $state.note).multilineTextAlignment(.trailing)
-                        if isFocused {
-                            Button { isFocused = false } label: { Image(systemName: "keyboard.chevron.compact.down") }
-                                .controlSize(.mini)
+                    VStack {
+                        HStack {
+                            Text("Note").foregroundColor(.secondary)
+                            TextFieldWithToolBarString(text: $state.note, placeholder: "", maxLength: 25)
+                            if isFocused {
+                                Button { isFocused = false } label: { Image(systemName: "keyboard.chevron.compact.down") }
+                                    .controlSize(.mini)
+                            }
+                        }.focused($isFocused)
+
+                        HStack {
+                            Spacer()
+                            Text("\(state.note.count) / 25")
+                                .foregroundStyle(.secondary)
                         }
-                    }.focused($isFocused)
+                    }
                     HStack {
                         Button {
                             state.useFPUconversion.toggle()

+ 6 - 1
FreeAPS/Sources/Modules/Bolus/View/BolusRootView.swift

@@ -65,7 +65,12 @@ extension Bolus {
                         HStack {
                             Text("Amount")
                             Spacer()
-                            TextFieldWithToolBar(text: $state.amount, placeholder: "0", shouldBecomeFirstResponder: true, numberFormatter: formatter)
+                            TextFieldWithToolBar(
+                                text: $state.amount,
+                                placeholder: "0",
+                                shouldBecomeFirstResponder: true,
+                                numberFormatter: formatter
+                            )
                             Text(state.amount > state.maxBolus ? "⚠️" : "U").foregroundColor(.secondary)
                         }
                     }

+ 12 - 2
FreeAPS/Sources/Modules/DataTable/View/DataTableRootView.swift

@@ -140,7 +140,12 @@ extension DataTable {
                         Section {
                             HStack {
                                 Text("New Glucose")
-                                TextFieldWithToolBar(text: $state.manualGlucose, placeholder: " ... ", shouldBecomeFirstResponder: true, numberFormatter: glucoseFormatter)
+                                TextFieldWithToolBar(
+                                    text: $state.manualGlucose,
+                                    placeholder: " ... ",
+                                    shouldBecomeFirstResponder: true,
+                                    numberFormatter: glucoseFormatter
+                                )
                                 Text(state.units.rawValue).foregroundStyle(.secondary)
                             }
                         }
@@ -247,7 +252,12 @@ extension DataTable {
                             HStack {
                                 Text("Amount")
                                 Spacer()
-                                TextFieldWithToolBar(text: $state.externalInsulinAmount, placeholder: "0", shouldBecomeFirstResponder: true, numberFormatter: insulinFormatter)
+                                TextFieldWithToolBar(
+                                    text: $state.externalInsulinAmount,
+                                    placeholder: "0",
+                                    shouldBecomeFirstResponder: true,
+                                    numberFormatter: insulinFormatter
+                                )
                                 Text("U").foregroundColor(.secondary)
                             }
                         }

+ 5 - 1
FreeAPS/Sources/Modules/FPUConfig/View/FPUConfigRootView.swift

@@ -62,7 +62,11 @@ extension FPUConfig {
                     HStack {
                         Text("Override With A Factor Of ")
                         Spacer()
-                        TextFieldWithToolBar(text: $state.individualAdjustmentFactor, placeholder: "0.5", numberFormatter: conversionFormatter)
+                        TextFieldWithToolBar(
+                            text: $state.individualAdjustmentFactor,
+                            placeholder: "0.5",
+                            numberFormatter: conversionFormatter
+                        )
                     }
                 }
 

+ 6 - 1
FreeAPS/Sources/Modules/ManualTempBasal/View/ManualTempBasalRootView.swift

@@ -19,7 +19,12 @@ extension ManualTempBasal {
                     HStack {
                         Text("Amount")
                         Spacer()
-                        TextFieldWithToolBar(text: $state.rate, placeholder: "0", shouldBecomeFirstResponder: true, numberFormatter: formatter)
+                        TextFieldWithToolBar(
+                            text: $state.rate,
+                            placeholder: "0",
+                            shouldBecomeFirstResponder: true,
+                            numberFormatter: formatter
+                        )
                         Text("U/hr").foregroundColor(.secondary)
                     }
                     Picker(selection: $state.durationIndex, label: Text("Duration")) {

+ 5 - 1
FreeAPS/Sources/Modules/NotificationsConfig/View/NotificationsConfigRootView.swift

@@ -105,7 +105,11 @@ extension NotificationsConfig {
                     HStack {
                         Text("Carbs Required Threshold")
                         Spacer()
-                        TextFieldWithToolBar(text: $state.carbsRequiredThreshold, placeholder: "0", numberFormatter: carbsFormatter)
+                        TextFieldWithToolBar(
+                            text: $state.carbsRequiredThreshold,
+                            placeholder: "0",
+                            numberFormatter: carbsFormatter
+                        )
                         Text("g").foregroundColor(.secondary)
                     }
                 }

+ 5 - 1
FreeAPS/Sources/Modules/PreferencesEditor/View/PreferencesEditorRootView.swift

@@ -62,7 +62,11 @@ extension PreferencesEditor {
                                         })
                                         Text(field.displayName)
                                     }
-                                    TextFieldWithToolBar(text: self.$state.sections[sectionIndex].fields[fieldIndex].decimalValue, placeholder: "0", numberFormatter: formatter)
+                                    TextFieldWithToolBar(
+                                        text: self.$state.sections[sectionIndex].fields[fieldIndex].decimalValue,
+                                        placeholder: "0",
+                                        numberFormatter: formatter
+                                    )
                                 case .insulinCurve:
                                     Picker(
                                         selection: $state.sections[sectionIndex].fields[fieldIndex].insulinCurveValue,

+ 121 - 0
FreeAPS/Sources/Views/TextFieldWithToolBar.swift

@@ -185,3 +185,124 @@ extension UIApplication {
         sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
     }
 }
+
+public struct TextFieldWithToolBarString: UIViewRepresentable {
+    @Binding var text: String
+    var placeholder: String
+    var textAlignment: NSTextAlignment = .right
+    var keyboardType: UIKeyboardType = .default
+    var autocapitalizationType: UITextAutocapitalizationType = .none
+    var autocorrectionType: UITextAutocorrectionType = .no
+    var shouldBecomeFirstResponder: Bool = false
+    var maxLength: Int? = nil
+    var isDismissible: Bool = true
+
+    public func makeUIView(context: Context) -> UITextField {
+        let textField = UITextField()
+        context.coordinator.textField = textField
+        textField.inputAccessoryView = isDismissible ? makeDoneToolbar(for: textField, context: context) : nil
+        textField.addTarget(context.coordinator, action: #selector(Coordinator.editingDidBegin), for: .editingDidBegin)
+        textField.delegate = context.coordinator
+        textField.text = text
+        textField.placeholder = placeholder
+        textField.textAlignment = textAlignment
+        textField.keyboardType = keyboardType
+        textField.autocapitalizationType = autocapitalizationType
+        textField.autocorrectionType = autocorrectionType
+        textField.adjustsFontSizeToFitWidth = true
+        return textField
+    }
+
+    private func makeDoneToolbar(for textField: UITextField, context: Context) -> UIToolbar {
+        let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))
+        let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
+        let doneButton = UIBarButtonItem(
+            image: UIImage(systemName: "keyboard.chevron.compact.down"),
+            style: .done,
+            target: textField,
+            action: #selector(UITextField.resignFirstResponder)
+        )
+        let clearButton = UIBarButtonItem(
+            image: UIImage(systemName: "trash"),
+            style: .plain,
+            target: context.coordinator,
+            action: #selector(Coordinator.clearText)
+        )
+
+        toolbar.items = [clearButton, flexibleSpace, doneButton]
+        toolbar.sizeToFit()
+        return toolbar
+    }
+
+    public func updateUIView(_ textField: UITextField, context: Context) {
+        if textField.text != text {
+            textField.text = text
+        }
+
+        textField.textAlignment = textAlignment
+        textField.keyboardType = keyboardType
+        textField.autocapitalizationType = autocapitalizationType
+        textField.autocorrectionType = autocorrectionType
+
+        if shouldBecomeFirstResponder, !context.coordinator.didBecomeFirstResponder {
+            if textField.window != nil, textField.becomeFirstResponder() {
+                context.coordinator.didBecomeFirstResponder = true
+            }
+        } else if !shouldBecomeFirstResponder, context.coordinator.didBecomeFirstResponder {
+            context.coordinator.didBecomeFirstResponder = false
+        }
+    }
+
+    public func makeCoordinator() -> Coordinator {
+        Coordinator(self, maxLength: maxLength)
+    }
+
+    public final class Coordinator: NSObject {
+        var parent: TextFieldWithToolBarString
+        var textField: UITextField?
+        let maxLength: Int?
+        var didBecomeFirstResponder = false
+
+        init(_ parent: TextFieldWithToolBarString, maxLength: Int?) {
+            self.parent = parent
+            self.maxLength = maxLength
+        }
+
+        @objc fileprivate func clearText() {
+            parent.text = ""
+            textField?.text = ""
+        }
+
+        @objc fileprivate func editingDidBegin(_ textField: UITextField) {
+            DispatchQueue.main.async {
+                textField.moveCursorToEnd()
+            }
+        }
+    }
+}
+
+extension TextFieldWithToolBarString.Coordinator: UITextFieldDelegate {
+    public func textField(
+        _ textField: UITextField,
+        shouldChangeCharactersIn range: NSRange,
+        replacementString string: String
+    ) -> Bool {
+        if let maxLength = parent.maxLength {
+            // Get the current text, including the proposed change
+            let currentText = textField.text ?? ""
+            let newLength = currentText.count + string.count - range.length
+            if newLength > maxLength {
+                return false
+            }
+        }
+
+        DispatchQueue.main.async {
+            if let textFieldText = textField.text as NSString? {
+                let newText = textFieldText.replacingCharacters(in: range, with: string)
+                self.parent.text = newText
+            }
+        }
+
+        return true
+    }
+}