SwiftUI Dismissing The Keyboard

Apple added a new modifier to SwiftUI in iOS 16 to customize how scrollable content like lists and forms dismiss the keyboard.

Focus State (iOS 15)

In iOS 15, you can use the focus state property wrapper to control keyboard focus:

private struct FeedbackForm: View {
  enum Field: Hashable {
    case email
    case text
  }
  
  @FocusState private var focus: Field?
  @State private var email = ""
  @State private var text = ""

  var body: some View {
    Form {
      Section("Your details") {
        TextField("email", text: $email)
          .focused($focus, equals: .email)
      }
      Section("Feedback") {
        TextEditor(text: $text)
          .focused($focus, equals: .text)
      }
    }
  }
}

Feedback form with iPhone keyboard visible

This makes it possible to dismiss the keyboard in response to a button or gesture action by setting the focus state to nil. For example, using a single tap on the form:

Form { ... }
.onTapGesture {
  focus = nil
}

What I want though is to dismiss the keyboard if I start scrolling the form.

scrollDismissesKeyboard (iOS 16)

The scrollDismissesKeyboard view modifier, added in iOS 16 and macOS 13, has four possible values which control how scrolling the view affects the keyboard:

  • automatic: context specific behaviour
  • immediately: dismiss the keyboard as soon as the content starts to scroll.
  • interactively: allow the user to dismiss the keyboard by scrolling it down off the screen.
  • never: scrolling never dismisses the keyboard.

This works much like the keyboardDismissMode of UIScrollView. The documentation is a little confusing about the default behaviour:

By default, a TextEditor is interactive while other kinds of scrollable content always dismiss the keyboard on a scroll when linked against iOS 16 or later.

In my experience, testing with iOS 16, scrolling a form or list containing text fields and text editor views never dismisses the keyboard by default.

Add the view modifier to the list or form (or a containing view). For example, to dismiss the keyboard as soon as the user starts to scroll:

Form { ... }
.scrollDismissesKeyboard(.immediately)

The interactive mode lets the user dismiss the keyboard by scrolling it down off the display:

Form { ... }
.scrollDismissesKeyboard(.interactively)

Feedback form on iPhone scrolled down so that keyboard is partially offscreen

This works like the .interactive keyboard dismiss mode of UIScrollView.