Search code examples
swiftuiuitextfielduitextfielddelegateresignfirstresponderuiviewrepresentable

SwiftUI Dismiss Keyboard from UITextField


So for my own reasons, I need the full control that UITextField and its delegates would normally offer, but the screen it's being added to is written with SwiftUI. Needs a .decimal keyboard, so no Return Key.

I have only 2 issues remaining, 1 of which I hope to resolve here. Attempting to add a call to resign first responder and add it to a VStack on the page basically disables the UITextField, since I can't bring up the keyboard.

How can I dismiss this keyboard without adding an arbitrary extra button to the page?

Example Code:

Struct CustomTextView: UIViewRepresentable {
    /// Insert init, updateView, binding variable, coordinator, etc
    func makeView() -> UITextField {
        var textField = UITextField()
        textField.delegate = context.coordinator
        /// Set up rest of textfield parameters such as Font, etc.
        return textField
    }
}

extension CustomTextView {
    class Coordinator: NSObject, UITextFieldDelegate {
        /// UITextfield delegate implementations, extra reference to binding variable, etc
        /// Primarily textField.shouldChangeCharactersInRange
    }
}

struct ContentView: View {
    @State viewModel: ViewModel
    var body: some View {
        VStack {
            CustomTextView($viewModel.parameter)
            /// Other views
        }
        .onTap {
            /// Attempting to add the generic call to UIApplication for resignFirstResponder here does not allow CustomTextView to ever hold it even when tapped in
        }
    }
}

I can't give all the code for privacy reasons, but this should be enough to establish my issue.


Solution

  • So I found a trick on my own with an epiphany overnight.

    First, I would like to share to anyone else a very basic reason why inb4cookies solution wasn't quite adequate. While I had already tried adding a resignFirstResponder call like it to the onTap of the background stack, it was triggering the onTap for the VStack when I was clicking the field.

    This is likely because I am using a UITextField as the back end for this component and not a SwiftUI TextField.

    However, it was partially used in the final solution. I still applied it, but there is an extra step.

    VStack {
                CustomTextView($viewModel.parameter)
                .onTap {/*Insert literally any compiling code here*/ }
                /// Other views
            }
            .onTap {
                self.hideKeyboard()
            }
    

    You'll see that above, there is an extra onTap. I tested it with a print statement, but this will override the onTap for the VStack and prevent the keyboard from being dismissed right after it is brought up. Tapping anywhere else on the VStack still closes it, except for Buttons. But I can always add hideKeyboard to those buttons if needed.