Observing changes @FocusState variable not working as expected

Viewed 518

I want to validate the user's changes in a specific textfield and thus need to hook in when this textfield loses focus. I am using this year's introduced .focused(_:equals:) modifier with an enum and a @FocusState.

Observing my optional @FocusState var, which is nil when the screen first loads, with .onChange(of:perform:), this is only called once, when @FocusState changes from nil to a value–but not when it changes to another enum value.

The latter is expected though, and once this happens I could check what the previous field was–at least that's my approach for now.

Why is this approach not working–and is there a better way to go about this?

struct FocusView: View {
    @State private var nameLast = "Ispum"
    @State private var phoneNumber = "+41 79 888 88 88"
    
    enum Field {
        case nameLast
        case phoneNumber
    }
    
    @FocusState private var focusedField: Field?
    @State private var prevFocusedField: Field?
    
    var body: some View {
        VStack {
            TextField("Last Name", text: $nameLast)
                .focused($focusedField, equals: .nameLast)
            TextField("Phone Number", text: $phoneNumber, prompt: Text(""))
                .focused($focusedField, equals: .nameLast)
        }
        .onChange(of: focusedField) { newFocusField in
            //only called once–when `focusedField` changes its state from `nil` to a value
            print("Old value: \(prevFocusedField)")
            print("New value: \(focusedField)")
            
            if prevFocusedField == .phoneNumber {
                //Validation
            }
            prevFocusedField = newFocusField
        }
        .textFieldStyle(.roundedBorder)
        .padding()
    }
}
1 Answers

There is a typo in your code (probably copy-paste)

TextField("Phone Number", text: $phoneNumber, prompt: Text(""))
    .focused($focusedField, equals: .nameLast)  // << here !!

you should use different value for second field:

VStack {
    TextField("Last Name", text: $nameLast)
        .focused($focusedField, equals: .nameLast)
    TextField("Phone Number", text: $phoneNumber, prompt: Text(""))
        .focused($focusedField, equals: .phoneNumber)    // << fix !!
}

Tested with Xcode 13 / iOS 15

Related