wandres.dev
SWIFTUI ESENCIAL · vistas y estado

Estado: @State y @Binding

SwiftUI redibuja la interfaz cuando el estado cambia. Domina @State (ahora una macro en iOS 26) y @Binding para conexiones de dos vías entre vistas.

⏱ 16 min

Aquí SwiftUI cobra vida. El estado es el dato que, al cambiar, hace que la interfaz se actualice sola. No mueves elementos a mano: cambias un valor, y SwiftUI redibuja lo que dependa de él. Entender el flujo de datos es entender SwiftUI.

🎯 Al terminar esta lección sabrás
  • Qué es una “fuente de la verdad”.
  • @State para el estado propio de una vista.
  • @Binding para conexiones de dos vías.
  • El flujo de datos que hace reactiva la UI.

La idea central: estado → UI

SwiftUI mantiene una regla: la interfaz es una función del estado. Cuando el estado cambia, la vista se recalcula. Tú no tocas la pantalla; tocas el dato.

struct Contador: View {
    @State private var cuenta = 0

    var body: some View {
        VStack {
            Text("Cuenta: \(cuenta)")
            Button("Sumar") { cuenta += 1 }
        }
    }
}

Al pulsar el botón, cuenta cambia y SwiftUI vuelve a llamar a body — el Text muestra el nuevo valor. Automático.

@State: el estado propio de la vista

@State marca un dato que pertenece a esa vista y que, al cambiar, la redibuja. Úsalo para estado simple y local (un contador, un texto de un campo, si un toggle está activo):

@State private var texto = ""
@State private var activo = false
ℹ️
@State es una macro en iOS 26

Novedad 2026: @State pasó a ser una macro que inicializa el valor de forma perezosa, solo una vez, en lugar de recrearlo en cada reinicialización de la vista. En la práctica no cambias cómo lo escribes, pero ganas rendimiento gratis, sobre todo con objetos @Observable (Nivel 3). Márcalo siempre private: el estado propio no debería tocarse desde fuera.

@Binding: una conexión de dos vías

Cuando una vista hija necesita leer y escribir un estado que vive en el padre, usas @Binding. Es una referencia al estado, no una copia:

struct Interruptor: View {
    @Binding var encendido: Bool      // conexión al estado del padre
    var body: some View {
        Toggle("Encendido", isOn: $encendido)
    }
}

struct Panel: View {
    @State private var luz = false     // la fuente de la verdad
    var body: some View {
        VStack {
            Interruptor(encendido: $luz)     // pasa el binding con $
            Text(luz ? "💡 encendida" : "🌑 apagada")
        }
    }
}
El símbolo $ es la clave

Fíjate en el $: luz es el valor (un Bool), pero $luz es el binding (la conexión de dos vías a ese valor). Cuando un componente pide un Binding —como Toggle(isOn:) o TextField(text:)—, le pasas $algo. El hijo lo modifica y el cambio sube solo al padre, que redibuja. Una única fuente de la verdad, muchas vistas conectadas a ella. Este patrón —estado arriba, bindings hacia abajo— es la columna vertebral de toda app SwiftUI.

Bindings con componentes del sistema

Muchos controles piden un binding para reflejar y cambiar el estado:

@State private var nombre = ""
@State private var volumen = 0.5

TextField("Tu nombre", text: $nombre)
Slider(value: $volumen)
Toggle("Notificaciones", isOn: $activo)

Escribes en el TextField y nombre se actualiza; mueves el Slider y volumen cambia. Sin código de “cuando cambie, actualiza”: el binding lo hace.

💡
¿@State o algo más?

@State es para estado simple y local de una vista (tipos de valor: Bool, String, Int, structs pequeñas). Para modelos de datos compartidos entre varias vistas, o lógica compleja, usarás clases @Observable (Nivel 3). Regla rápida: dato local y simple → @State; modelo compartido → @Observable.

⚔️ Haz una UI reactiva
  1. Crea un contador con @State y dos botones (+ y −).
  2. Añade un TextField con @State y muestra abajo lo que escribes en vivo.
  3. Crea una vista hija con @Binding var y pásale $algo desde el padre.
  4. Usa un Toggle(isOn: $...) en el hijo y muestra el estado en el padre.
  5. Observa que solo cambias valores: nunca “actualizas la pantalla” a mano.