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)
}
}
}
}
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 behaviourimmediately
: 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)
This works like the .interactive
keyboard dismiss mode of UIScrollView
.