-
Notifications
You must be signed in to change notification settings - Fork 689
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add observer, notify on ClearButttonTapped to set state value in Deci… #295
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested the functionality:
Bolus:
Enter an amount, tap Clear
and Done
, observed the amount field being successfully cleared ✅
Carbs, Fat, Protein
For each of the input fields:
Enter an amount, tap Clear
and Done
, observed the amount field being successfully cleared ✅
Entering something for two or three of carbs, fat or protein: Tapping Clear
clears all three entries.
From my testing point of view: LGTM!
Would be good to have someone else take a look at the code changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am actually able to somehow get different results too, with values not being cleared, please let me try to reproduce 👀
Testing again:
Bolus:
-
Enter an amount, tap
Clear
andDone
, observed the amount field being successfully cleared ✅ -
Enter an amount, tap
Done
and then mark the entered amount and tapClear
, observed the amount field not being successfully cleared 🚫
Carbs, Fat, Protein
-
Enter an amount, tap
Clear
andDone
, observed the amount field being successfully cleared ✅ -
Enter an amount, tap
Done
and then mark the entered amount and tapClear
, observed the amount field not being successfully cleared 🚫
…teringg Objserver in textField
I updated and tested again. As far as I can tell, the problem still remains when tapping Done before Clear. I also made another observation, which is unrelated to this PR: And when a value is removed by tapping Would it be possible to really clear the amount field, instead of setting to zero? And when selecting a previously entered value, can we force either a right hand side cursor position (to more easily delete the existing value with backspace), or marking the value so that it is replaced when entering a new value? |
Going to the way-back machine. Initially on FAX, there was a bug where the cursor wouldn't move to the end of the entered characters. (I think this was a temporary issue with a particular xcode/ios version that no longer exists. We had it in Loop-dev and then we didn't - long time ago.) My memory says Ivan popped out the Clear button as a quick solution, so you could tap clear and then type in new characters. |
I changed this PR to go to alpha. |
@kskandis , I tested your last commit, and now it works exactly as requested 🥳
And the correct behaviour is observed for catbs/fat/protein, bolus, TT Target/Duration, Profile (SMB minutes), Manual Temp Basal Amount, Logg insulin/glucose, Preferences, and probably all over. The only exception so far is the |
Yes, the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good now that the cursor for notes is forced to the right too.
Not sure if it is possible to add Clear/Done either? I’ll accept it as is, but feel free to make changes.
Looks perfect now! Was it a hack to get Clear/Done for the Notes keyboard? Looks good and more consistent anyway. Perhaps we could do away with the hide keyboard button then? Could you check in the commit history who added that? I think this was done recently. Just to check the reasons why it was added instead of Clear/Done buttons. |
…tent with other input fields
Thanks, that’s perfect! Do this, and let’s 🚢 it! 🚀 |
Sorry, my response was out-of-sync with your review. Fix is resolved in this commit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! 🚀
Tagging @polscm32 to please give this a look and review. We've come across a similar issue and he has fixed this prior, cannot find the specfic commit right now though. |
The approach in the pull request for creating a Decimal Text Field and handling the toolbar is interesting, but in my opinion, it’s too complicated. The use of the Notification Broadcaster for event handling seems a bit excessive and can be simplified. This would make the code simpler and more readable. I have also fixed the problem in my personal repo and can provide the code if desired. I followed the implementation of the DismissibleKeyboardTextField struct in Loop. It’s simpler in my opinion, and button actions are managed directly in the Coordinator without the need for a Notification Broadcaster. The only thing I needed to change was the addition of a clear button, which is actually pretty straightforward (and some refactoring to adapt to our codebase). |
Yes, please do! |
@kskandis I believe this is the implementation @polscm32 is referring to from his private project. Click to show codeimport SwiftUI
import UIKit
public struct TextFieldWithToolBar: UIViewRepresentable {
@Binding var text: Decimal
var placeholder: String
var textColor: UIColor
var textAlignment: NSTextAlignment
var keyboardType: UIKeyboardType
var autocapitalizationType: UITextAutocapitalizationType
var autocorrectionType: UITextAutocorrectionType
var shouldBecomeFirstResponder: Bool
var maxLength: Int?
var isDismissible: Bool
var textFieldDidBeginEditing: (() -> Void)?
var numberFormatter: NumberFormatter
public init(
text: Binding<Decimal>,
placeholder: String,
textColor: UIColor = .label,
textAlignment: NSTextAlignment = .right,
keyboardType: UIKeyboardType = .decimalPad,
autocapitalizationType: UITextAutocapitalizationType = .none,
autocorrectionType: UITextAutocorrectionType = .no,
shouldBecomeFirstResponder: Bool = false,
maxLength: Int? = nil,
isDismissible: Bool = true,
textFieldDidBeginEditing: (() -> Void)? = nil,
numberFormatter: NumberFormatter = NumberFormatter()
) {
_text = text
self.placeholder = placeholder
self.textColor = textColor
self.textAlignment = textAlignment
self.keyboardType = keyboardType
self.autocapitalizationType = autocapitalizationType
self.autocorrectionType = autocorrectionType
self.shouldBecomeFirstResponder = shouldBecomeFirstResponder
self.maxLength = maxLength
self.isDismissible = isDismissible
self.textFieldDidBeginEditing = textFieldDidBeginEditing
self.numberFormatter = numberFormatter
self.numberFormatter.numberStyle = .decimal
}
public func makeUIView(context: Context) -> UITextField {
let textField = UITextField()
textField.inputAccessoryView = isDismissible ? makeDoneToolbar(for: textField, context: context) : nil
textField.addTarget(context.coordinator, action: #selector(Coordinator.textChanged), for: .editingChanged)
textField.addTarget(context.coordinator, action: #selector(Coordinator.editingDidBegin), for: .editingDidBegin)
textField.delegate = context.coordinator
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 text != 0 {
textField.text = numberFormatter.string(from: text as NSNumber)
} else {
textField.text = ""
}
textField.placeholder = placeholder
textField.textColor = textColor
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: TextFieldWithToolBar
let maxLength: Int?
var didBecomeFirstResponder = false
init(_ parent: TextFieldWithToolBar, maxLength: Int?) {
self.parent = parent
self.maxLength = maxLength
}
@objc fileprivate func textChanged(_ textField: UITextField) {
if let text = textField.text, let value = parent.numberFormatter.number(from: text)?.decimalValue {
parent.text = value
} else {
parent.text = 0
}
}
@objc fileprivate func clearText() {
parent.text = 0
}
@objc fileprivate func editingDidBegin(_ textField: UITextField) {
DispatchQueue.main.async {
textField.moveCursorToEnd()
}
}
}
}
extension TextFieldWithToolBar.Coordinator: UITextFieldDelegate {
public func textField(
_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String
) -> Bool {
guard let maxLength = maxLength else {
return true
}
let currentString: NSString = (textField.text ?? "") as NSString
let newString: NSString =
currentString.replacingCharacters(in: range, with: string) as NSString
return newString.length <= maxLength
}
public func textFieldDidBeginEditing(_: UITextField) {
parent.textFieldDidBeginEditing?()
}
}
extension UITextField {
func moveCursorToEnd() {
dispatchPrecondition(condition: .onQueue(.main))
let newPosition = endOfDocument
selectedTextRange = textRange(from: newPosition, to: newPosition)
}
}
extension UIApplication {
func endEditing() {
sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
} |
Thanks Dan! Side note: I have also rewritten this code entirely in SwiftUI but theres apparently a constraint bug in SwiftUI regarding the toolbar so I've decided to leave it like that although I prefer this over UIKit. The code itself is working though. Its just the console that throws an error 🤷🏻♂️ |
Looks good. Should we close this PR then, and @polscm32 create a new one with his code? I'm fine with that. |
I could do this tomorrow 😄 |
…utton always beeing disabled (nightscout#295)
This PR resolves “Clear” button for bolus and carb entries does not work as expected
#273
Observer / notify added to ClearButtonTapped action. On doneButtonTapped action, Observers are removed.
Tested on Simulator and iPhone SE