iosswiftuifocustextfielduikeyboardtype

SwiftUI Changing KeyboardType Per TextField


I have multiple TextFields and for one of them I need to switch the keyboardType to .numberPad. Obviously this is really easy using the .keyboardType(.numberPad) modifier, but the issue is that when the default keyboard is already shown it doesn't dismiss and then bring up the numberPad. Instead, it just switches immediately which causes the user to have to manually swipe up to see the TextField in which they will be entering numbers.

What I need to do is completely dismiss the default keyboard and then show the numberPad so that the correct TextField is pushed up on screen.

I've tried various modifiers for the numberField TextField such as:

.onAppear {
    isFocused = nil
    isFocused = .numberField
}

.onAppear {
    isFocused = nil
    sleep(1)
    isFocused = .numberField
}

.focused($isFocused, equals: nil)
.keyboardType(.numberPad)
.focused($isFocused, equals: .numberField)

None work. The keyboard switches, but the screen does not scroll to the correct field.

As this is a multi-field form, I am using the submit button to switch between fields like this:

TextField("", text: $field1)
    .focused($isFocused, equals: .field1)
    .keyboardType(.default)
    .onSubmit {
        if isFocused == .field1 {
            isFocused = .numberField
        }
    }
.submitLabel(.next)

TextField("", text: $numberField)
    .focused($isFocused, equals: .numberField)
    .keyboardType(.numberPad)

If I manually dismiss the default keyboard for field1 and then tap on the numberField TextField, the numberPad comes up and the field focuses correctly. So, I need to somehow automatically dismiss the default keyboard and then show the numberPad so the correct field is focused on screen for the user.


Solution

  • In order to dismiss an existing keyboard and then show a new type of keyboard, while maintaining keyboard avoidance and focusing on the correct TextField, you need to use DispatchQueue as follows:

    TextField("", text: $field1)
        .focused($isFocused, equals: .field1)
        .keyboardType(.default)
        .onSubmit {
            if isFocused == .field1 {
                isFocused = nil
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                    isFocused = .numberField
                }
            }
        }
    

    By using DispatchQueue you allow the app to keep running, while completely dismissing the keyboard, waiting x seconds (in my example it's 0.3 seconds which feels pretty instantaneous) and then bringing up the new keyboard.