Is there a way to ad a onEditingChanged in a secure text field In swiftUi?

Viewed 3570

I was making a login page for an app I am working on and was adding offset to a textfield to it would move up when they keyboard popped up but when I tried it on the secure field I was not able to select onEditingChanged like I did on the regular textfield. I used this code for the Regular Textfeild:

TextField("Username", text: $username, onEditingChanged: { edit in  if edit == true {self.EditingMode = true} else {self.EditingMode = false }})
        .padding()
        .background(lightGreyColor)
        .cornerRadius(5.0)
        .padding(.bottom, 20)

And for the Secure Field I tried to use the same thing but it wouldn't work

SecureField("Password", text: $password)
  .padding()
  .background(lightGreyColor)
  .cornerRadius(5.0)
  .padding(.bottom, 20)
4 Answers

You could use the onTapGesture modifier

.onTapGesture {
    print("Password Field clicked")
}
@State private var isEditing: Bool = false

var body: some View {

   SecureField("Password", text: $password, onCommit: { isEditing = false })
        .onTapGesture {
            isEditing = true
        }
   }
}

This is an example using the popular Introspect library. I have used the isSelected property with CustomTextFieldStyle which is not included as implementation, but you can use the property as you wish.

struct CustomSecureTextField: View {
    
    class TFDelegate: NSObject, UITextFieldDelegate, ObservableObject {
        
        @Published var isSelected = false
        
        func textFieldDidBeginEditing(_ textField: UITextField) {
            withAnimation {
                isSelected = true
            }
        }
        
        func textFieldDidEndEditing(_ textField: UITextField) {
            withAnimation {
                isSelected = false
            }
        }
    }
    
    @StateObject var delegate = TFDelegate()
    
    var title: String = ""
    
    @Binding var text: String
    
    var onCommit: ()->()
    
    var body: some View {
        SecureField(title, text: $text, onCommit: onCommit)
            .textFieldStyle(CustomTextFieldStyle(isSelected: delegate.isSelected))
            .introspectTextField { textField in
                textField.delegate = self.delegate
            }
    }
    
    init(_ title: String = "", text: Binding<String>, onCommit: (()->())? = nil) {
        self.title = title
        self._text = text
        self.onCommit = onCommit ?? {}
    }
    
    init(_ title: String = "", text: Binding<String?>, onCommit: (()->())? = nil) {
        self.title = title
        self._text = Binding(get: {
            text.wrappedValue ?? ""
        }, set: {
            text.wrappedValue = $0.isEmpty ? nil : $0
        })
        self.onCommit = onCommit ?? {}
    }
    
    func editingCallback(isEditing: Bool) {
        withAnimation {
            delegate.isSelected = isEditing
        }
    }
}

The best way I've found to handle secure field focus is using a tap gesture that is recognized simultaneously with the normal SecureField gestures. This way a user can tap once to start focus and you still get a callback from the tap gesture.

@State private var isFocused: Bool = false
SecureField("", text: $text, onCommit: onCommit)
    .simultaneousGesture(
        TapGesture()
            .onEnded {
                isFocused = true
            }
    )
    .onSubmit {
        isFocused = false
    }
Related