@FocusState — che cos'è, gestione del focus e della tastiera in SwiftUI

Autore: IT Sectr Pubblicato: 2026-06-26 Tempo di lettura: 9 min

@FocusState è un property wrapper in SwiftUI, introdotto in iOS 15, che consente di controllare programmaticamente il focus di input su campi di testo e altri elementi. Prima della sua comparsa, gli sviluppatori dovevano usare UIViewRepresentable per accedere ai metodi UIKit becomeFirstResponder e resignFirstResponder. @FocusState risolve questo problema in modo nativo: si lega una proprietà a un campo tramite il modificatore .focused(), dopodiché l'impostazione o la rimozione del focus avviene con una semplice assegnazione di valore. Secondo Apple Developer Documentation — FocusState (2025), @FocusState supporta due modalità: Bool per la gestione semplice (focus attivo o meno) ed enum per più campi, dove ogni case corrisponde a un campo di input specifico.

Punti chiave

  • @FocusState — un property wrapper per la gestione programmatica del focus in SwiftUI, disponibile da iOS 15.
  • Modalità Bool — per un singolo campo usa @FocusState var isFocused: Bool con .focused($isFocused).
  • Modalità Enum — per più campi usa un enum conforme al protocollo FocusStateValue e .focused($field, equals: .fieldName).
  • Nascondere la tastiera — imposta il focus su nil o false per nascondere la tastiera.
  • Focus automatico — imposta il valore iniziale in .onAppear per mostrare la tastiera all'apertura dello schermo.

Cos'è @FocusState in SwiftUI

@FocusState è un property wrapper che lega lo stato del focus a un campo di input specifico o a un altro elemento focusabile in SwiftUI. A differenza di UIKit, dove la gestione del focus avviene tramite i metodi becomeFirstResponder e resignFirstResponder, SwiftUI utilizza un approccio dichiarativo: si dichiara uno stato (@FocusState) e lo si lega a un elemento tramite il modificatore .focused(). Modificare lo stato cambia automaticamente il focus.

Prima dell'introduzione di @FocusState in iOS 15, gli sviluppatori dovevano creare wrapper UIViewRepresentable attorno a UITextField o usare librerie di terze parti. @FocusState è integrato direttamente in SwiftUI e funziona con TextField, TextEditor, SecureField e SearchField. Questo rende il codice più pulito, riduce il numero di ponti UIKit e migliora la testabilità.

Secondo WWDC Session 10136 — What's new in SwiftUI (2024), @FocusState utilizza il sistema di preference keys di SwiftUI per passare le informazioni di focus tra gli elementi. Quando un campo riceve il focus, SwiftUI aggiorna automaticamente la proprietà @FocusState associata, consentendo di reagire alle modifiche del focus nel codice.

Gestione del focus con Bool

Il modo più semplice per usare @FocusState è con il tipo Bool. Quando un campo è focalizzato, la proprietà è true. Quando il focus viene perso — false. È possibile forzare il focus impostandolo a true o rimuoverlo impostandolo a false.

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

In questo esempio, isEmailFocused diventa automaticamente true quando l'utente tocca il campo di testo e false quando la tastiera viene nascosta. I pulsanti consentono di gestire il focus in modo programmatico — utile per tastiere personalizzate, pulsanti "Avanti" e situazioni in cui è necessario nascondere forzatamente la tastiera dopo l'invio del modulo.

Gestione del focus con Enum per più campi

Per moduli con più campi, @FocusState supporta un enum conforme al protocollo FocusStateValue (o Hashable). Ogni case dell'enum corrisponde a un campo specifico. Ciò consente di spostare il focus tra i campi — ad esempio, quando l'utente preme "Avanti" sulla tastiera per passare al campo successivo.

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() }
        }
    }
}

Nota il modificatore .onSubmit — viene chiamato quando l'utente preme "Return" sulla tastiera. All'interno di .onSubmit spostiamo focusedField al campo successivo, che sposta automaticamente il focus. L'ultimo campo chiama submitForm() per inviare il modulo.

Mostrare e nascondere la tastiera

@FocusState fornisce un modo semplice per nascondere la tastiera — basta impostare la proprietà su nil (per enum) o false (per Bool). Tuttavia, a volte è necessario nascondere la tastiera senza legarla a un campo specifico — ad esempio, toccando uno spazio vuoto. In questo caso, esistono diversi approcci.

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("Fatto") {
                        isFocused = false
                    }
                }
            }
    }
}

Il modificatore .toolbar con placement .keyboard aggiunge un pulsante sopra la tastiera. Questo è un pattern UX standard in iOS per nascondere la tastiera. Un approccio alternativo è usare .onTapGesture sul VStack radice per reimpostare il focus toccando lo sfondo.

Focus e validazione dei moduli

@FocusState si combina perfettamente con la validazione dei moduli. Un pattern tipico: dopo aver premuto il pulsante "Invia", validare tutti i campi e impostare il focus sul primo campo con errore. Questo migliora l'esperienza utente — l'utente vede immediatamente quale campo deve essere corretto.

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("Invia") { validateAndSubmit() }
        }
    }
    
    func validateAndSubmit() {
        if name.isEmpty {
            focusedField = .name
            return
        }
        if phone.isEmpty {
            focusedField = .phone
            return
        }
        // invia modulo
    }
}

In questo esempio, se il campo name è vuoto, il focus si sposta su di esso e l'utente vede immediatamente dov'è l'errore. Se name è compilato, viene controllato phone. Questo è un comportamento naturale per i moduli — l'utente compila i campi dall'alto verso il basso e la validazione segue lo stesso ordine.

Errori comuni con @FocusState

L'errore più comune è cercare di usare @FocusState con un tipo che non è conforme a Hashable. @FocusState richiede che il tipo della proprietà sia Hashable (Bool ed enum opzionali sono già conformi). Se si sta cercando di usare una struttura personalizzata, assicurarsi che implementi Hashable.

  • Modificatore .focused() dimenticato — @FocusState da solo non gestisce il focus. È necessario legarlo a un campo tramite .focused($property) o .focused($property, equals: .case).
  • Più @FocusState in una sola View — per più campi, usa un singolo @FocusState con un enum, non più proprietà @FocusState. Più proprietà Bool non saranno sincronizzate tra loro.
  • Modificare @FocusState al di fuori del thread principale — @FocusState dovrebbe essere modificato solo sul thread principale, come tutte le proprietà UI in SwiftUI. Le operazioni asincrone devono passare a MainActor prima di modificarlo.
  • Reimpostazione del focus durante la ricostruzione della View — se la View viene ricostruita, @FocusState potrebbe reimpostarsi. Usa il modificatore .id() per un'identificazione stabile della View.
swift
// ❌ Errore: due @FocusState Bool invece di enum
@FocusState var isNameFocused: Bool
@FocusState var isEmailFocused: Bool

// ✅ Corretto: singolo enum @FocusState
enum Field: Hashable { case name; case email }
@FocusState var focusedField: Field?

Domande frequenti

Da quali versioni di iOS è disponibile @FocusState?

@FocusState è disponibile da iOS 15, iPadOS 15, macOS 12, tvOS 15 e watchOS 8. Per progetti che supportano iOS 14 e versioni precedenti, usa UIViewRepresentable con UITextField e becomeFirstResponder, o librerie di terze parti con implementazione personalizzata della gestione del focus.

Si può usare @FocusState con UIViewRepresentable personalizzato?

Sì, per farlo è necessario implementare il supporto per FocusState nel UIViewRepresentable personalizzato tramite il protocollo UIViewRepresentable. La vista personalizzata deve avere becomeFirstResponder e resignFirstResponder. SwiftUI collegherà automaticamente @FocusState a questi metodi se si specifica il modificatore .focused().

Perché @FocusState non funziona con TextField in List?

In List o Form, le celle possono essere riutilizzate, il che rompe il legame @FocusState con il campo. Soluzione: aggiungi il modificatore .id() con un identificatore univoco per ogni TextField. Ad esempio: .id(fieldName). Questo forza SwiftUI a creare un'istanza di View separata per ogni campo.

Come nascondere la tastiera toccando uno spazio vuoto?

Aggiungi .onTapGesture al contenitore radice (VStack, ZStack) e reimposta il focus: focusedField = nil. Tuttavia, .onTapGesture potrebbe bloccare i tocchi sui pulsanti interni — usa un contenitore con .contentShape(Rectangle()) e .onTapGesture su di esso, o un UIKitBackgroundView personalizzato.

Come animare la comparsa della tastiera con @FocusState?

@FocusState non fornisce un'API diretta per l'animazione della tastiera — questo è un comportamento di sistema iOS. Tuttavia, puoi reagire alle modifiche del focus con .onChange(of: focusedField) o .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) per animazioni personalizzate del contenuto.

Riepilogo

  • @FocusState — un property wrapper nativo SwiftUI per la gestione del focus di input, disponibile da iOS 15.
  • Due modalità — Bool per un singolo campo, enum Hashable per più campi del modulo.
  • Modificatore .focused() — obbligatorio per legare @FocusState a un campo di input specifico.
  • Controllo programmatico — impostare il valore su nil o false nasconde la tastiera.
  • Validazione dei moduli — @FocusState consente di impostare il focus sul primo campo con errore dopo la validazione.
  • Enum per più campi — un singolo @FocusState con enum è preferibile a più proprietà Bool.
  • iOS 15+ — per versioni precedenti, usa UIViewRepresentable con becomeFirstResponder.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche