SwiftUI
TextField
iOS Development
Focus Detection
Swift Programming

How to detect when a TextField loses the focus in SwiftUI for iOS?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Detecting when a SwiftUI TextField loses focus is usually about validation, saving edits, or advancing a form. In current SwiftUI, the cleanest way is to bind focus explicitly with @FocusState and react when that state changes from active to inactive.

Detect blur with @FocusState

On iOS 15 and later, @FocusState is the standard API for focus management. You bind the field to a Boolean or enum-backed focus value, then watch that binding for changes.

swift
1import SwiftUI
2
3struct UsernameView: View {
4    @State private var username = ""
5    @FocusState private var usernameFocused: Bool
6
7    var body: some View {
8        TextField("Username", text: $username)
9            .textFieldStyle(.roundedBorder)
10            .focused($usernameFocused)
11            .onChange(of: usernameFocused) { isFocused in
12                if !isFocused {
13                    print("Username field lost focus")
14                }
15            }
16            .padding()
17    }
18}

When the Boolean changes to false, the field has lost focus. That can happen because the user tapped elsewhere, moved to another field, dismissed the keyboard, or because your code changed focus programmatically.

Manage several fields with one enum

A single Boolean works for one field, but multi-field forms are easier to reason about with an enum.

swift
1import SwiftUI
2
3struct SignupView: View {
4    enum Field: Hashable {
5        case email
6        case password
7    }
8
9    @State private var email = ""
10    @State private var password = ""
11    @FocusState private var focusedField: Field?
12
13    var body: some View {
14        VStack {
15            TextField("Email", text: $email)
16                .focused($focusedField, equals: .email)
17
18            SecureField("Password", text: $password)
19                .focused($focusedField, equals: .password)
20        }
21        .textFieldStyle(.roundedBorder)
22        .padding()
23        .onChange(of: focusedField) { newField in
24            if newField != .email {
25                print("Email field lost focus")
26            }
27        }
28    }
29}

This pattern scales better because only one field is focused at a time, and the state is represented explicitly.

Validate when focus leaves the field

Blur detection is often used to delay validation until the user finishes editing. That avoids noisy validation on every keystroke.

swift
1import SwiftUI
2
3struct EmailInputView: View {
4    @State private var email = ""
5    @State private var errorText = ""
6    @FocusState private var emailFocused: Bool
7
8    var body: some View {
9        VStack(alignment: .leading) {
10            TextField("Email", text: $email)
11                .textFieldStyle(.roundedBorder)
12                .keyboardType(.emailAddress)
13                .focused($emailFocused)
14                .onChange(of: emailFocused) { isFocused in
15                    if !isFocused {
16                        errorText = email.contains("@") ? "" : "Please enter a valid email."
17                    }
18                }
19
20            if !errorText.isEmpty {
21                Text(errorText)
22                    .font(.caption)
23                    .foregroundColor(.red)
24            }
25        }
26        .padding()
27    }
28}

That keeps the form responsive while still giving feedback at the right moment.

Programmatic focus changes count too

Because focus is a normal state value, you can set it in code and reuse the same blur handling.

swift
Button("Done") {
    emailFocused = false
}

That makes keyboard dismissal, wizard-style navigation, and validation flow consistent. The blur logic does not care whether focus changed because of a tap or because your code updated the state.

Older deployment targets

If you support versions before @FocusState, SwiftUI does not offer the same direct focus API. In that case, a UIViewRepresentable wrapper around UITextField can expose UIKit delegate callbacks such as textFieldDidEndEditing.

swift
1class Coordinator: NSObject, UITextFieldDelegate {
2    var onEndEditing: () -> Void
3
4    init(onEndEditing: @escaping () -> Void) {
5        self.onEndEditing = onEndEditing
6    }
7
8    func textFieldDidEndEditing(_ textField: UITextField) {
9        onEndEditing()
10    }
11}

That fallback is more verbose, so it is best reserved for older targets or hybrid UIKit-SwiftUI screens.

Common Pitfalls

The most common mistake is confusing onSubmit with focus loss. onSubmit fires when the user triggers submit behavior, such as pressing return, but a field can lose focus in several other ways.

Another issue is modeling each field with a separate Boolean when the screen really has one active field at a time. Enum-backed focus state is usually simpler and avoids contradictory states.

Developers also forget that programmatic focus changes trigger the same blur logic. If validation should happen only for user-driven blur, add an explicit guard rather than assuming the callback source.

Finally, keep deployment targets in mind. @FocusState is the right answer for modern SwiftUI, but older iOS versions require UIKit bridging if true blur detection is still needed.

Summary

  • Use @FocusState to track whether a SwiftUI TextField currently has focus.
  • Observe the focus binding with onChange and react when it becomes inactive.
  • Prefer an enum-backed focus state for multi-field forms.
  • Run validation on blur when you want quieter, more natural feedback.
  • Use a UIKit bridge only when older deployment targets make @FocusState unavailable.

Course illustration
Course illustration

All Rights Reserved.