How to dismiss keyboard in SwiftUI

Apr 28, 2024#swiftui

In iOS development, handling the dismissal of the keyboard is essential for a smooth and user-friendly experience. Here are some reasons why:

  • If the keyboard remains visible after they finish entering text, it can obstruct other content.
  • Users expect that tapping outside a text field or submit will dismiss the keyboard.
  • In forms or data entry screens, users typically move from one input field to another.

Understanding keyboards and the concept of the first responder is crucial. When an object becomes the first responder (such as a text field when it is tapped), it is ready to accept input from the user, usually in the form of keyboard input. When you relinquish this status, meaning the object will no longer respond to keyboard input, and the keyboard will be dismissed.

There are two main approaches to dismiss the keyboard in SwiftUI:

Using @FocusState (iOS 15.0+)

@FocusState is a property wrapper that helps manage keyboard focus for one or more views. It was introduced in iOS 15 and provides a way to control which view in your interface should have the keyboard focus. Here’s how it works and the differences between the two modes:

Single Field Focus Control (Boolean Mode)

When you want to control the focus of a single text field, you can bind @FocusState to a Boolean value. If the Boolean is true, the text field becomes the first responder and gains focus, bringing up the keyboard. If it’s false, the text field resigns as the first responder, and the keyboard is dismissed.

import SwiftUI

struct ContentView: View {
    @State private var text: String = ""
    @FocusState var isFocused: Bool
    
    var body: some View {
        Form {
            Section {
                TextField("Enter text", text: $text)
                    .focused($isFocused)
            }
            
            Section {
                Button {
                    isFocused = false
                } label: {
                    Text("Submit")
                }
            }
        }
    }
}

Multiple Fields Focus Control (Enum Mode)

When your interface has multiple text fields and you want to move the focus between them, you can use an optional enum with @FocusState. Each case of the enum represents a different text field. Setting the @FocusState variable to a specific case moves the focus to the associated text field. Setting it to nil removes the focus from all fields, effectively dismissing the keyboard.

import SwiftUI

struct ContentView: View {
    @State private var firstName: String = ""
    @State private var lastName: String = ""
    
    enum Field: Hashable {
        case firstName
        case lastName
    }
    @FocusState private var focusedField: Field?
    
    var body: some View {
        Form {
            Section {
                TextField("Enter First Name", text: $firstName)
                    .focused($focusedField, equals: .firstName)
                
                TextField("Enter Last Name", text: $lastName)
                    .focused($focusedField, equals: .lastName)
            }
            
            Section {
                Button {
                    if firstName.isEmpty {
                        focusedField = .firstName
                    } else if lastName.isEmpty {
                        focusedField = .lastName
                    } else {
                        focusedField = nil
                    }
                } label: {
                    Text("Submit")
                }
            }
        }
    }
}

The main difference between the two modes is that the Boolean mode is used for single text field focus control, while the enum mode is used for managing focus across multiple text fields. Both modes allow for programmatic control of the keyboard focus, which can enhance the user experience by guiding the user through a form or interface logically and efficiently.

Using resignFirstResponder

For versions prior to iOS 15 you have to rely on resignFirstResponder method, provided by the UIResponder class in UIKit, which is the superclass of many iOS interface-related classes, including UIView, UIViewController, and UIApplication.

This method is used to resign the first responder status from the current responder object, typically a UIResponder subclass like a UITextField or a UITextView.

When you’re using UIApplication.shared to sendAction to #selector(UIResponder.resignFirstResponder), you’re telling the application to find the first responder and send the action to it. It’s a way to programmatically control the keyboard without needing a reference to the specific text field or text view that is currently active.

import SwiftUI
import UIKit

struct ContentView: View {
    @State private var text: String = ""
    
    var body: some View {
        Form {
            Section {
                TextField("Enter text", text: $text)
            }
            
            Section {
                Button {
                    // Handle submission
                    UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
                } label: {
                    Text("Submit")
                }
            }
        }
    }
}

This approach is useful when you have multiple text fields and want a single action to dismiss the keyboard, regardless of which one is active.