Skip to content

Mastering Focus in SwiftUI: Using .focused Modifier

SwiftUI’s .focused modifier allows you to control and observe the focus state of your views. This is particularly useful for managing user input fields and ensuring a smooth user experience. In this tutorial, we’ll explore how to use the .focused modifier with unique and interesting examples that are simple to understand for developers of all levels.

Introduction to .focused

The .focused modifier binds the focus state of a view to a Boolean state value. When the bound value is true, the view receives focus; when false, it loses focus. This allows you to programmatically manage the focus state of your views.

Basic Example

Let’s start with a basic example where we manage the focus state of a TextField in a form, including a visual indicator for when the field is focused.

import SwiftUI

struct FocusedExampleView: View {
    @State private var username: String = ""
    @State private var password: String = ""
    @FocusState private var usernameFieldIsFocused: Bool
    @FocusState private var passwordFieldIsFocused: Bool
    @State private var showPasswordHint = false

    var body: some View {
        VStack(spacing: 20) {
            TextField("Username", text: $username)
                .focused($usernameFieldIsFocused)
                .padding()
                .background(usernameFieldIsFocused ? Color.yellow.opacity(0.3) : Color.clear)
                .cornerRadius(10)
                .overlay(
                    RoundedRectangle(cornerRadius: 10)
                        .stroke(usernameFieldIsFocused ? Color.blue : Color.gray, lineWidth: 2)
                )

            SecureField("Password", text: $password)
                .focused($passwordFieldIsFocused)
                .padding()
                .background(passwordFieldIsFocused ? Color.yellow.opacity(0.3) : Color.clear)
                .cornerRadius(10)
                .overlay(
                    RoundedRectangle(cornerRadius: 10)
                        .stroke(passwordFieldIsFocused ? Color.blue : Color.gray, lineWidth: 2)
                )

            if showPasswordHint {
                Text("Password must be at least 8 characters long.")
                    .foregroundColor(.red)
            }

            Button("Submit") {
                validateForm()
            }
            .padding()
            .background(Color.blue)
            .foregroundColor(.white)
            .cornerRadius(10)
        }
        .padding()
    }

    private func validateForm() {
        if username.isEmpty {
            usernameFieldIsFocused = true
        } else if password.count < 8 {
            passwordFieldIsFocused = true
            showPasswordHint = true
        } else {
            showPasswordHint = false
            print("Form submitted")
        }
    }
}

Explanation

  1. State Variables: We use @State variables to store the input values for the username and password fields, and to manage the visibility of the password hint.
  2. FocusState: We use @FocusState property wrappers to track the focus state of the username and password fields.
  3. TextField and SecureField: The TextField for the username and the SecureField for the password are each bound to their respective focus states using the .focused modifier.
  4. Visual Indicators: We change the background color and border color of the fields to indicate when they are focused.
  5. Form Validation: The validateForm function checks if the username is empty or if the password is less than 8 characters long. It sets the focus to the appropriate field and displays a hint if necessary.
  6. Submit Button: The “Submit” button triggers the form validation logic.

Advanced Example: Managing Multiple Focused Fields

In a more complex form, you might need to manage multiple fields and their focus states. Let’s extend our example to include an email field and implement a more sophisticated focus management, with visual indicators for each field.

import SwiftUI

struct AdvancedFocusedExampleView: View {
    @State private var username: String = ""
    @State private var email: String = ""
    @State private var password: String = ""
    @FocusState private var usernameFieldIsFocused: Bool
    @FocusState private var emailFieldIsFocused: Bool
    @FocusState private var passwordFieldIsFocused: Bool
    @State private var showPasswordHint = false

    var body: some View {
        VStack(spacing: 20) {
            TextField("Username", text: $username)
                .focused($usernameFieldIsFocused)
                .padding()
                .background(usernameFieldIsFocused ? Color.yellow.opacity(0.3) : Color.clear)
                .cornerRadius(10)
                .overlay(
                    RoundedRectangle(cornerRadius: 10)
                        .stroke(usernameFieldIsFocused ? Color.blue : Color.gray, lineWidth: 2)
                )

            TextField("Email", text: $email)
                .focused($emailFieldIsFocused)
                .padding()
                .background(emailFieldIsFocused ? Color.yellow.opacity(0.3) : Color.clear)
                .cornerRadius(10)
                .overlay(
                    RoundedRectangle(cornerRadius: 10)
                        .stroke(emailFieldIsFocused ? Color.blue : Color.gray, lineWidth: 2)
                )
                .keyboardType(.emailAddress)
                .autocapitalization(.none)

            SecureField("Password", text: $password)
                .focused($passwordFieldIsFocused)
                .padding()
                .background(passwordFieldIsFocused ? Color.yellow.opacity(0.3) : Color.clear)
                .cornerRadius(10)
                .overlay(
                    RoundedRectangle(cornerRadius: 10)
                        .stroke(passwordFieldIsFocused ? Color.blue : Color.gray, lineWidth: 2)
                )

            if showPasswordHint {
                Text("Password must be at least 8 characters long.")
                    .foregroundColor(.red)
            }

            Button("Submit") {
                validateForm()
            }
            .padding()
            .background(Color.blue)
            .foregroundColor(.white)
            .cornerRadius(10)
        }
        .padding()
    }

    private func validateForm() {
        if username.isEmpty {
            usernameFieldIsFocused = true
        } else if !isValidEmail(email) {
            emailFieldIsFocused = true
        } else if password.count < 8 {
            passwordFieldIsFocused = true
            showPasswordHint = true
        } else {
            showPasswordHint = false
            print("Form submitted")
        }
    }

    private func isValidEmail(_ email: String) -> Bool {
        // Simple email validation
        let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
        let emailPredicate = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
        return emailPredicate.evaluate(with: email)
    }
}

Explanation

  1. Multiple Fields: We apply the same visual indicator logic to the username, email, and password fields.
  2. Form Validation: The validation logic ensures the correct field is focused based on the validation outcome.
  3. Email Field: The email field has its own focus state and visual indicators.

By adding these visual cues, users can easily identify which field is currently active, enhancing the user experience.

Conclusion

Using the .focused modifier in SwiftUI allows you to control and observe the focus state of your views, enhancing the user experience by managing input fields efficiently. By following these examples, you can implement sophisticated focus management in your SwiftUI applications.

 

Back To Top