@FocusState — ce este, gestionarea focalizării și tastaturii în SwiftUI

Autor: IT Sectr Publicat: 2026-06-26 Timp de citire: 9 min

@FocusState — este un property wrapper în SwiftUI, introdus în iOS 15, care permite gestionarea programatică a focalizării intrării pe câmpuri text și alte elemente. Înainte de apariția sa, dezvoltatorii trebuiau să folosească UIViewRepresentable pentru a accesa metodele UIKit becomeFirstResponder și resignFirstResponder. @FocusState rezolvă această problemă în mod nativ: legați o proprietate de câmp prin modificatorul .focused(), după care setarea sau resetarea focalizării are loc prin simpla atribuire a valorii. Conform Apple Developer Documentation — FocusState (2025), @FocusState suportă două moduri: Bool pentru gestionare simplă (focalizare există sau nu) și enum pentru mai multe câmpuri, unde fiecare case corespunde unui anumit câmp de introducere.

Principalele

  • @FocusState — property wrapper pentru gestionarea programatică a focalizării în SwiftUI, disponibil de la iOS 15.
  • Modul Bool — pentru un singur câmp folosește @FocusState var isFocused: Bool cu .focused($isFocused).
  • Modul Enum — pentru mai multe câmpuri folosește enum cu protocolul FocusStateValue și .focused($field, equals: .fieldName).
  • Ascunderea tastaturii — setează focalizarea la nil sau false pentru a ascunde tastatura.
  • Focalizare automată — setează valoarea inițială în .onAppear pentru a afișa tastatura la deschiderea ecranului.

Ce este @FocusState în SwiftUI

@FocusState — este un property wrapper care leagă starea de focalizare de un anumit câmp de introducere sau alt element focusabil în SwiftUI. Spre deosebire de UIKit, unde gestionarea focalizării se face prin metodele becomeFirstResponder și resignFirstResponder, SwiftUI folosește o abordare declarativă: declarați starea (@FocusState) și o legați de element prin modificatorul .focused(). Schimbarea stării modifică automat focalizarea.

Înainte de apariția @FocusState în iOS 15, dezvoltatorii trebuiau să creeze îmbrăcăminți UIViewRepresentable în jurul UITextField sau să folosească biblioteci terțe. @FocusState este integrat direct în SwiftUI și funcționează cu TextField, TextEditor, SecureField și SearchField. Acest lucru face codul mai curat, reduce numărul de punți UIKit și îmbunătățește testabilitatea.

Conform WWDC Session 10136 — What's new in SwiftUI (2024), @FocusState folosește sistemul de chei de preferință SwiftUI pentru a transmite informații despre focalizare între elemente. Când un câmp primește focalizarea, SwiftUI actualizează automat proprietatea @FocusState asociată, permițând reacția la schimbările de focalizare în cod.

Gestionarea focalizării cu Bool

Cel mai simplu mod de a folosi @FocusState — tipul Bool. Când câmpul este focalizat, proprietatea este true. Când focalizarea pleacă — false. Puteți forța setarea focalizării atribuind true sau o puteți reseta atribuind false.

swift
struct LoginForm: View {
    @State var email = ""
    @FocusState var isEmailFocused: Bool
    
    var body: some View {
        VStack {
            TextField("Email", text: $email)
                .focused($isEmailFocused)
            
            Button("Arată tastatura") {
                isEmailFocused = true
            }
            Button("Ascunde tastatura") {
                isEmailFocused = false
            }
        }
    }
}

În acest exemplu, isEmailFocused devine automat true când utilizatorul atinge câmpul text și false când tastatura este ascunsă. Butoanele permit gestionarea programatică a focalizării — util pentru tastaturi personalizate, butoane „Următorul” și situații când trebuie forțată ascunderea tastaturii după trimiterea formularului.

Gestionarea focalizării cu Enum pentru mai multe câmpuri

Pentru formulare cu mai multe câmpuri, @FocusState suportă un enum conform protocolului FocusStateValue (sau Hashable). Fiecare case a enum-ului corespunde unui anumit câmp. Aceasta permite comutarea focalizării între câmpuri — de exemplu, la apăsarea „Următorul” pe tastatură pentru a trece la următorul câmp.

swift
struct RegistrationForm: View {
    enum Field: Hashable {
        case email
        case password
        case confirmPassword
    }
    
    @State var email = ""
    @State var password = ""
    @State var confirmPassword = ""
    @FocusState var focusedField: Field?
    
    var body: some View {
        Form {
            TextField("Email", text: $email)
                .focused($focusedField, equals: .email)
                .onSubmit { focusedField = .password }
            
            SecureField("Password", text: $password)
                .focused($focusedField, equals: .password)
                .onSubmit { focusedField = .confirmPassword }
            
            SecureField("Confirm", text: $confirmPassword)
                .focused($focusedField, equals: .confirmPassword)
                .onSubmit { submitForm() }
        }
    }
}

Observați modificatorul .onSubmit — este apelat când utilizatorul apasă „Return” pe tastatură. În interiorul .onSubmit comutăm focusedField pe următorul câmp, ceea ce mută automat focalizarea. Ultimul câmp apelează submitForm() pentru trimiterea formularului.

Ascunderea și afișarea tastaturii

@FocusState oferă o modalitate simplă de a ascunde tastatura — este suficient să setați proprietatea la nil (pentru enum) sau false (pentru Bool). Uneori însă trebuie să ascundeți tastatura fără a o lega de un anumit câmp — de exemplu, la atingerea unui loc gol. În acest caz, există mai multe abordări.

swift
struct DismissKeyboardView: View {
    @State var text = ""
    @FocusState var isFocused: Bool
    
    var body: some View {
        TextField("Enter text", text: $text)
            .focused($isFocused)
            .toolbar {
                ToolbarItemGroup(placement: .keyboard) {
                    Spacer()
                    Button("Gata") {
                        isFocused = false
                    }
                }
            }
    }
}

Modificatorul .toolbar cu placement .keyboard adaugă un buton deasupra tastaturii. Acesta este un model standard UX în iOS pentru ascunderea tastaturii. O abordare alternativă — folosirea .onTapGesture pe VStack-ul principal pentru a reseta focalizarea la atingerea fundalului.

Focalizare și validare formular

@FocusState se combină excelent cu validarea formularului. Modelul tipic: după apăsarea butonului „Trimite”, verificați toate câmpurile și setați focalizarea pe primul câmp cu eroare. Acest lucru îmbunătățește experiența utilizatorului — utilizatorul vede imediat care câmp trebuie corectat.

swift
struct ValidatedForm: View {
    enum Field: Hashable { case name; case phone }
    
    @State var name = ""
    @State var phone = ""
    @FocusState var focusedField: Field?
    @State var errors: [String] = []
    
    var body: some View {
        Form {
            TextField("Name", text: $name)
                .focused($focusedField, equals: .name)
            TextField("Phone", text: $phone)
                .focused($focusedField, equals: .phone)
            
            Button("Trimite") { validateAndSubmit() }
        }
    }
    
    func validateAndSubmit() {
        if name.isEmpty {
            focusedField = .name
            return
        }
        if phone.isEmpty {
            focusedField = .phone
            return
        }
        // trimite formularul
    }
}

În acest exemplu, când câmpul name este gol, focalizarea se mută pe el și utilizatorul vede imediat unde este eroarea. Dacă name este completat, se verifică phone. Acesta este un comportament natural pentru formulare — utilizatorul completează câmpurile de sus în jos, iar validarea urmează aceeași ordine.

Erori tipice cu @FocusState

Cea mai frecventă eroare — încercarea de a folosi @FocusState cu un tip care nu respectă Hashable. @FocusState necesită ca tipul proprietății să fie Hashable (Bool și enum-urile opționale sunt deja conforme). Dacă încercați să folosiți o structură personalizată, asigurați-vă că implementează Hashable.

  • Modificatorul .focused() uitat — @FocusState nu gestionează focalizarea de unul singur. Trebuie obligatoriu să-l legați de câmp prin .focused($property) sau .focused($property, equals: .case).
  • Mai multe @FocusState în același View — pentru mai multe câmpuri folosiți un singur @FocusState cu enum, nu mai multe proprietăți @FocusState. Mai multe proprietăți Bool nu vor fi sincronizate între ele.
  • Modificarea @FocusState în afara thread-ului principal — @FocusState trebuie modificat doar pe thread-ul principal, la fel ca toate proprietățile UI în SwiftUI. Operațiile asincrone trebuie să comute pe MainActor înainte de modificare.
  • Resetarea focalizării la reconstruire — dacă View-ul este reconstruit, @FocusState se poate reseta. Folosiți modificatorul .id() pentru identificarea stabilă a View-ului.
swift
// ❌ Eroare: două @FocusState Bool în loc de enum
@FocusState var isNameFocused: Bool
@FocusState var isEmailFocused: Bool

// ✅ Corect: un singur enum @FocusState
enum Field: Hashable { case name; case email }
@FocusState var focusedField: Field?

Întrebări frecvente

De la ce versiuni iOS este disponibil @FocusState?

@FocusState este disponibil de la iOS 15, iPadOS 15, macOS 12, tvOS 15 și watchOS 8. Pentru proiecte care suportă iOS 14 și mai jos, folosiți UIViewRepresentable cu UITextField și becomeFirstResponder, sau biblioteci terțe cu implementare personalizată a gestionării focalizării.

Se poate folosi @FocusState cu UIViewRepresentable personalizat?

Da, pentru aceasta trebuie să implementați suportul pentru FocusState în UIViewRepresentable personalizat prin protocolul UIViewRepresentable. View-ul personalizat trebuie să aibă becomeFirstResponder și resignFirstResponder. SwiftUI va lega automat @FocusState de aceste metode dacă specificați modificatorul .focused().

De ce @FocusState nu funcționează cu TextField în List?

În List sau Form, celulele pot fi reutilizate, ceea ce strică legătura @FocusState cu câmpul. Soluție: adăugați modificatorul .id() cu un identificator unic pentru fiecare TextField. De exemplu: .id(fieldName). Acest lucru forțează SwiftUI să creeze o instanță View separată pentru fiecare câmp.

Cum ascund tastatura la atingerea unui loc gol?

Adăugați .onTapGesture pe containerul principal (VStack, ZStack) și resetați focalizarea: focusedField = nil. Totuși, .onTapGesture poate bloca atingerile pe butoanele din interior — folosiți un container cu .contentShape(Rectangle()) și .onTapGesture pe el, sau un UIViewBackgroundView personalizat.

Cum animez apariția tastaturii cu @FocusState?

@FocusState nu oferă o API directă pentru animarea tastaturii — acesta este un comportament de sistem iOS. Puteți totuși reacționa la schimbările de focalizare cu .onChange(of: focusedField) sau .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) pentru animație personalizată a conținutului.

Rezumat

  • @FocusState — property wrapper nativ SwiftUI pentru gestionarea focalizării intrării, disponibil de la iOS 15.
  • Două moduri — Bool pentru un singur câmp, Hashable enum pentru mai multe câmpuri de formular.
  • Modificatorul .focused() — obligatoriu pentru legarea @FocusState de un anumit câmp de introducere.
  • Gestionare programatică — setarea valorii nil sau false ascunde tastatura.
  • Validare formulare — @FocusState permite setarea focalizării pe primul câmp cu eroare după verificare.
  • Enum pentru mai multe câmpuri — un singur @FocusState cu enum este preferabil mai multor proprietăți Bool.
  • iOS 15+ — pentru versiuni mai vechi folosiți UIViewRepresentable cu becomeFirstResponder.

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și