Stewart Lynch
Stewart Lynch

Reputation: 995

SwiftUI dismiss keyboard when tapping segmentedControl

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)
    }
}

Upvotes: 10

Views: 4663

Answers (4)

Youngerman
Youngerman

Reputation: 9

For SwiftUI 3 use FocusState and .focused(…)

Upvotes: -1

Asperi
Asperi

Reputation: 258345

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 !!
    }

Upvotes: 2

lawrencebensaid
lawrencebensaid

Reputation: 111

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) }

Upvotes: 7

RPatel99
RPatel99

Reputation: 8106

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())
}

Upvotes: 10

Related Questions