What's the best way to know when a user is editing a SecureField in SwiftUI?

What's the best way to know when a user is editing a SecureField in SwiftUI?

Our normal way of detecting this is via an .onTapGesture modifier attached to the SecureField.

However if the user is using a hardware keyboard, they don't have to tap into the field. Instead they can use arrow keys or tab to select a particular SecureField and begin editing it, in which case our .onTapGesture event does not happen.

I've been looking for an event like .onBeginEditing, or .willBeginEditing, however I don't see that there's any modifiers like this available for SecureField.

Our designers want us to change the border thickness of the field that's currently being edited so it's obvious, and of course we want to scroll that field into view if it's off-screen. How can we do stuff like this in SwiftUI with SecureField?

Thanks.

Replies

Just want to make something clear: I am not trying to know when the field is focused, just when it's being edited (the insertion point is blinking). Let me know if that's feasible.
The only solution I've been able to come up with so far is to record the CGRect of the view using GeometryReader then compare that rect against rect of the object of these notifications:

Code Block    
private var editingPublisher: AnyPublisher<Bool, Never> {
        Publishers.Merge(
            NotificationCenter.default
                .publisher(for: UITextField.textDidBeginEditingNotification)
                .map { notification in
                    guard
                        let textField = notification.object as? UITextField,
                        let originA = environmentObj.rectMap[id]?.origin,
                        let originB = textField.globalFrame?.origin,
                        originA.y == originB.y
                    else { return nil }
                    self.environmentObj.activeFrame = textField.globalFrame=
                    return true
                },
            NotificationCenter.default
                .publisher(for: UITextField.textDidEndEditingNotification)
                .map { notification in
                    guard
                        let textField = notification.object as? UITextField,
                        let originA = environmentObj.rectMap[id]?.origin,
                        let originB = textField.globalFrame?.origin,
                        originA.y == originB.y
                    else { return nil }
                    self.environmentObj.activeFrame = nil
                    return false
                }
        )
        .compactMap { $0 }
        .eraseToAnyPublisher()


Then add this modifier to the view:

Code Block
.onReceive(editingPublisher) { isEditing in
if isEditing { // set border thicker }
else { // set border skinny }
}


However this solution is inelegant, inefficient, and it depends on a platform-specific API (UIKit) which means it won't work in a native macOS app without resorting to duplicating this logic separately for the Mac.

So that's why I'm here asking what is the correct way to do this? Surely there's a better way, right? What is it?
you could try something like this:

Code Block
@State var theText = ""
@State var isInSecureField = false
SecureField("enter your password", text: Binding<String>(
get: { self.theText },
set: { self.theText = $0
print("editing a SecureField")
self.isInSecureField = true
}
))

workingdogintokyo wrote:

You could try something like this:

Code Block
@State var theText = ""
@State var isInSecureField = false
SecureField("enter your password", text: Binding<String>(
get: { self.theText },
set: { self.theText = $0
print("editing a SecureField")
self.isInSecureField = true
}
))
That won't work. That would only tell us when someone has actually changed the value of the field.

We want to know when someone started editing the field—i.e. when the blinking insertion point is visible—regardless of whether or not they've made any changes yet.