Skip to content
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

Closed
wants to merge 6 commits into from
6 changes: 1 addition & 5 deletions FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,7 @@ extension AddCarbs {
}
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)
}
RightAdjustedTextField(text: $state.note, textAlignment: .right)
}.focused($isFocused)
HStack {
Button {
Expand Down
150 changes: 150 additions & 0 deletions FreeAPS/Sources/Views/DecimalTextField.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Combine
import Foundation
import SwiftUI

struct DecimalTextField: UIViewRepresentable {
Expand Down Expand Up @@ -86,6 +87,8 @@ struct DecimalTextField: UIViewRepresentable {
}

private(set) var isEditing = false
private(set) var _beganEditing = false
private(set) var _rightAlign = true
private var editingCancellable: AnyCancellable?

func resetEditing() {
Expand Down Expand Up @@ -134,20 +137,75 @@ struct DecimalTextField: UIViewRepresentable {
) {
// Format value with formatter at End Editing
textField.text = parent.formatter.string(for: parent.value)
if textField.text == "0"
{
textField.text = ""
}
isEditing = false
NotificationBroadcaster.shared.removeListeners(for: "ClearButtonTappedObserver")
}

func textFieldDidBeginEditing(_: UITextField) {
_beganEditing =
true // if we change cursor position here (with DispatchQueue), cursor jumps ; instead, do in DidChangeSelection
NotificationBroadcaster.shared.register(event: "ClearButtonTappedObserver") { [self] _ in
clearButtonTappedDidUpdate(object: self)
}
}

func textFieldDidChangeSelection(_ textField: UITextField) {
if _beganEditing {
_beganEditing = false
if _rightAlign {
let position = textField.endOfDocument
textField.selectedTextRange = textField.textRange(from: position, to: position)
}
}
}

func clearButtonTappedDidUpdate(object _: Any) {
parent.value = 0
}
}
}

// MARK: Singleton NotificationBroadcaster class

class NotificationBroadcaster {
static let shared = NotificationBroadcaster()

private var listeners: [String: [(Any) -> Void]] = [:]

func register(event: String, listener: @escaping (Any) -> Void) {
if listeners[event] == nil {
listeners[event] = []
}
listeners[event]?.append(listener)
}

func notify(event: String, object: Any?) {
listeners[event]?.forEach { $0(object ?? ()) }
}

func removeListeners(for event: String) {
listeners[event] = nil
}
}

// MARK: extension for done button

extension UITextField {
private var broadcaster: NotificationBroadcaster {
NotificationBroadcaster.shared
}

@objc func doneButtonTapped(button _: UIBarButtonItem) {
resignFirstResponder()
}

@objc func clearButtonTapped(button _: UIBarButtonItem) {
text = ""
broadcaster.notify(event: "ClearButtonTappedObserver", object: self)
}
}

Expand All @@ -158,3 +216,95 @@ extension UIApplication {
sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}

// MARK: RightAdjustedTextField UITextField implementation

struct RightAdjustedTextField: UIViewRepresentable {
@Binding var text: String
var textAlignment: NSTextAlignment

class Coordinator: NSObject, UITextFieldDelegate {
var parent: RightAdjustedTextField

init(parent: RightAdjustedTextField) {
self.parent = parent
}

// Position cursor at end of text
func textFieldDidBeginEditing(_ textField: UITextField) {
DispatchQueue.main.async {
let endPosition = textField.endOfDocument
textField.selectedTextRange = textField.textRange(from: endPosition, to: endPosition)
}
NotificationBroadcaster.shared.register(event: "ClearButtonTappedObserver") { [self] _ in
clearButtonTappedDidUpdate(object: self)
}
}

func textFieldDidEndEditing(
_ textField: UITextField,
reason _: UITextField.DidEndEditingReason
) {
textField.text = parent.text
NotificationBroadcaster.shared.removeListeners(for: "ClearButtonTappedObserver")
}

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}

func clearButtonTappedDidUpdate(object _: Any) {
parent.text = ""
}

@objc func textFieldEditingChanged(_ textField: UITextField) {
parent.text = textField.text ?? ""
}
}

func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}

func makeUIView(context: Context) -> UITextField {
let textField = UITextField()
textField.delegate = context.coordinator
textField.translatesAutoresizingMaskIntoConstraints = false
textField.setContentHuggingPriority(.defaultLow, for: .horizontal)
textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
textField.textAlignment = textAlignment
textField.addTarget(context.coordinator, action: #selector(Coordinator.textFieldEditingChanged(_:)), for: .editingChanged)
let toolBar = UIToolbar(frame: CGRect(
x: 0,
y: 0,
width: textField.frame.size.width,
height: 44
))
let clearButton = UIBarButtonItem(
title: NSLocalizedString("Clear", comment: "Clear button"),
style: .plain,
target: self,
action: #selector(textField.clearButtonTapped(button:))
)
let doneButton = UIBarButtonItem(
title: NSLocalizedString("Done", comment: "Done button"),
style: .done,
target: self,
action: #selector(textField.doneButtonTapped(button:))
)
let space = UIBarButtonItem(
barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace,
target: nil,
action: nil
)
toolBar.setItems([clearButton, space, doneButton], animated: true)
textField.inputAccessoryView = toolBar
return textField
}

func updateUIView(_ uiView: UITextField, context _: Context) {
uiView.text = text
uiView.textAlignment = textAlignment
}
}