10

I have a TextField in SwiftUI that needs to use a different keyboard depending on the value of a @State variable determined by a SegementedControl() picker.

How can I dismiss the keyboard (like send an endEditing event) when the user taps a different segment? I need to do this because I want to change the keyboard type and if the textField is the responder, the keyboard won't change.

I have this extension:

extension UIApplication {
    func endEditing() {
        sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
}

And I can do something like

UIApplication.shared.endEditing()

But I don't know where or how to call this when the user taps a different segment.

I have tried putting a tapGesture on the Picker and the keyboard does dismiss, but the tap does not pass through to the picker so it does not change.

Code snippet here:

@State private var type:String = "name"

. . .

Form {
    Section(header: Text("Search Type")) {
        Picker("", selection: $type) {
            Text("By Name").tag("name")
            Text("By AppId").tag("id")
        }.pickerStyle(SegmentedPickerStyle())
    }

    Section(header: Text("Enter search value")) {
        TextField(self.searchPlaceHolder, text: $searchValue)
            .keyboardType(self.type == "name" ? UIKeyboardType.alphabet : UIKeyboardType.numberPad)
    }
}

4 Answers 4

10

Attach a custom Binding to the Picker that calls endEditing() whenever it is set:

Section(header: Text("Search Type")) {
    Picker("", selection: Binding(get: {
        self.type
    }, set: { (res) in
        self.type = res
        UIApplication.shared.endEditing()
    })) {
        Text("By Name").tag("name")
        Text("By AppId").tag("id")
    }.pickerStyle(SegmentedPickerStyle())
}
Sign up to request clarification or add additional context in comments.

1 Comment

Perfect. Thanks
7

Update since iOS 13 / iPadOS 13 was released.

Since there is now support for multiple windows in one app you need to loop through the UIWindows and end editing one-by-one.

UIApplication.shared.windows.forEach { $0.endEditing(false) }

Comments

2

SwiftUI 2.0

Now it can be done in more elegant way, with .onChange (actually it can be attached to any view, but at TextField looks appropriate, by intention)

TextField("Placeholder", text: $searchValue)
    .keyboardType(self.type == "name" ? UIKeyboardType.alphabet : UIKeyboardType.numberPad)
    .onChange(of: type) { _ in
        UIApplication.shared.endEditing()   // << here !!
    }

SwiftUI 1.0+

There are much similar to above approaches

a) requires import Combine...

TextField("Placeholder", text: $searchValue)
    .keyboardType(self.type == "name" ? UIKeyboardType.alphabet : UIKeyboardType.numberPad)
    .onChange(of: type) { _ in
        UIApplication.shared.endEditing()
    }
    .onReceive(Just(type)) { _ in
        UIApplication.shared.endEditing()   // << here !!
    }

b) ... and not

TextField("Placeholder", text: $searchValue)
    .keyboardType(self.type == "name" ? UIKeyboardType.alphabet : UIKeyboardType.numberPad)
    .onReceive([type].publisher) { _ in
        UIApplication.shared.endEditing()   // << here !!
    }

Comments

-1

For SwiftUI 3 use FocusState and .focused(…)

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.