SwiftUI: Switching Focus Between Text Fields

Mykhailo Moiseienko
3 min readJun 26, 2023

--

In iOS development, managing text field focus and enabling seamless navigation between multiple text fields is crucial for creating a smooth and intuitive user experience. In this article, we will explore how to achieve effortless focus switching in SwiftUI, making it easier for users to input data and interact with your app.

Understanding the Focus State

To manage focus in SwiftUI you should use the FocusState property wrapper that is available starting from iOS 15.

FocusState property wrapper handles several aspects:

  1. It tracks the currently focused view.
  2. It enables switching focus to a specific view.
  3. It allows for removing focus from all views, which leads to keyboard dismissal.

FocusState property wrapper is used in conjunction with 2 modifiers:

  • focused(_:) — modifies view by binding its focus state to the given Boolean state value. Use this modifier to cause the view to receive focus whenever the condition value is true. You can use this modifier to observe the focus state of a single view, or programmatically set and remove focus from the view.
struct ProfileView: View {
@State private var firstName: String = ""
@FocusState private var firstNameFieldIsFocused: Bool

var body: some View {
NavigationStack {
Form {
TextField("First Name", text: $firstName)
.focused($firstNameFieldIsFocused)
}
.onAppear {
firstNameFieldIsFocused = true
}
}
}
}
  • focused(_:equals:) — modifies view by binding its focus state to the given state value. Use this modifier to cause the view to receive focus whenever the binding equals the value.
struct ProfileView: View {
enum FocusableField: Hashable {
case firstName, lastName, email
}

@State private var firstName: String = ""
@State private var lastName: String = ""
@State private var email: String = ""

@FocusState private var focusedField: FocusableField?

var body: some View {
NavigationStack {
Form {
TextField("First Name", text: $firstName)
.focused($focusedField, equals: .firstName)

TextField("Last Name", text: $lastName)
.focused($focusedField, equals: .lastName)

TextField("Email", text: $email)
.keyboardType(.emailAddress)
.focused($focusedField, equals: .email)
}
.onAppear {
focus = .firstName
}
}
}
}

Switching Focus between Text Fields

When implementing a form that requires users to input multiple pieces of information, such as a registration form or a checkout process, switching focus between text fields is crucial. It allows users to effortlessly move through each field, filling in the required information without the need to manually tap on the next field each time.

Let’s consider a sample app that will consist of “Profile” screen allowing users to enter first name, last name, and email.

By following this code sample you will learn how to:

  • Focus on the first text field when the view appears.
  • Switch focus to the next text field when the user presses a Return Key.
  • Switch focus to a specific text field according to your logic.
struct ContentView: View {
enum FocusableField: Hashable, CaseIterable {
case firstName, lastName, email
}

@State private var firstName: String = ""
@State private var lastName: String = ""
@State private var email: String = ""

@FocusState private var focusedField: FocusableField?

var body: some View {
NavigationStack {
Form {
TextField("First Name", text: $firstName)
.focused($focusedField, equals: .firstName)

TextField("Last Name", text: $lastName)
.focused($focusedField, equals: .lastName)

TextField("Email", text: $email)
.keyboardType(.emailAddress)
.focused($focusedField, equals: .email)
}
.navigationTitle("Profile")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Save", action: save)
}
}
.onAppear(perform: focusFirstField)
.onSubmit(focusNextField)
}
}

private func focusFirstField() {
focusedField = FocusableField.allCases.first
}

private func focusNextField() {
switch focusedField {
case .firstName:
focusedField = .lastName
case .lastName:
focusedField = .email
case .email:
focusedField = nil
case .none:
break
}
}

private func save() {
if firstName.isEmpty {
focusedField = .firstName
} else if lastName.isEmpty {
focusedField = .lastName
} else if email.isEmpty {
focusedField = .email
} else {
// Save...
}
}
}

Thanks for reading! I hope you enjoyed the article. Subscribe to my Medium profile for more insightful content in the future.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Mykhailo Moiseienko
Mykhailo Moiseienko

Written by Mykhailo Moiseienko

📱 Senior iOS Software Engineer. ✍️ Writing about iOS Development, Swift, and SwiftUI.

Responses (2)

Write a response