The onSubmit view modifier (SwiftUI Release 3, iOS 15+) runs a closure when the user hits the
return key in a text field, search bar, or form. It is the declarative replacement for delegate
callbacks on submit.
Submit Triggers
onSubmit(of:) takes a trigger that scopes which submissions fire the closure: .search,
.text, or .form. With .search the closure runs only when the user submits the search
field.
struct SearchView: View {
@ObservedObject var viewModel: ViewModel
@Binding var query: String
var body: some View {
List(viewModel.messages, id: \.self) { message in
Text(message)
}
.searchable(text: $query)
.onSubmit(of: .search) {
viewModel.search(matching: query)
}
.navigationTitle("Search")
}
}
For TextField and SecureField, attach the modifier with the .text trigger. You can stack
several onSubmit modifiers with different triggers on one hierarchy, each with its own
closure.
Return Key Label
submitLabel changes the label of the return key on the software keyboard. SubmitLabel has
predefined values: done, go, send, join, route, search, return, next, and
continue.
TextField("query", text: $query)
.submitLabel(.send)
.onSubmit(of: .text) {
print(query)
}
Scopes
onSubmit can sit anywhere above the fields, so one closure can serve a whole form. Use
submitScope to exclude a view from triggering that shared submission while its condition is
true.
VStack {
TextField("phone", text: $viewModel.phone)
.submitScope(viewModel.phone.count > 11)
VStack {
TextField("email", text: $viewModel.email)
TextField("password", text: $viewModel.password)
}
}
.onSubmit(of: .text) {
viewModel.signUp()
}
Here the phone field never fires the sign-up closure while the scope condition holds; the other fields still do.