SwiftUI #25: Estado (@State)

Published: (January 9, 2026 at 03:18 PM EST)
4 min read
Source: Dev.to

Source: Dev.to

@State

@State es un property‑wrapper que envuelve un valor de tipo SomeType en una estructura State y notifica al sistema cuando ese valor cambia, de modo que la vista se actualice automáticamente.

Nota: La propiedad marcada con @State debería ser private porque representa el estado interno de la vista y no debe ser modificada por sus clientes.

struct ContentView: View {
    @State private var title: String = "Título inicial"

    var body: some View {
        VStack {
            Text(title)
            Button {
                title = "Otro título"
            } label: {
                Text("Modificar")
            }
        }
    }
}

En el ejemplo anterior, cuando title cambia, @State notifica el cambio al sistema y el contenido de body se vuelve a renderizar.

Binding

Binding es una referencia a un valor almacenado por un @State.

Para que una vista pueda modificar internamente el valor almacenado por un @State, se le pasa una referencia al State mediante su projected value, accesible añadiendo el prefijo $ al nombre de la variable.

struct ContentView: View {
    @State private var tituloFinal: String = "Título inicial"
    @State private var title: String = ""

    var body: some View {
        VStack {
            Text(tituloFinal)
                .padding(10)

            TextField("Ingrese título", text: $title)

            Button {
                tituloFinal = title
            } label: {
                Text("Actualizar título")
            }
        }
    }
}

Ejemplo de Binding en SwiftUI

Subvista que recibe un Binding

Cuando una vista necesita modificar el valor envuelto por un @State externo, se declara una variable de instancia de tipo Binding usando el property‑wrapper @Binding.

struct ContentSubview: View {
    var title: String
    @Binding var titleInput: String

    var body: some View {
        VStack {
            Text(title)
                .padding(10)

            TextField("Ingrese título", text: $titleInput)
        }
    }
}

Uso de la subvista desde la vista principal

struct ContentView: View {
    @State private var tituloFinal: String = "Título inicial"
    @State private var title: String = ""

    var body: some View {
        VStack {
            ContentSubview(title: tituloFinal, titleInput: $title)

            Button {
                tituloFinal = title
            } label: {
                Text("Actualizar título")
            }
        }
    }
}

Importante: Una propiedad @Binding siempre recibe su valor de una propiedad @State, por lo que no es necesario asignarle un valor por defecto.

State y Binding como estructuras

Swift permite acceder a la estructura subyacente del property‑wrapper usando un guión bajo (_) como prefijo del nombre de la variable.

Tabla de propiedades

PropiedadDescripción
wrappedValueEl valor manejado por @State. Se accede directamente mediante el nombre de la propiedad (sin _).
projectedValueUn Binding que actúa como referencia a State. Se accede mediante $ (equivalente a _nombre.projectedValue).
struct ContentSubview: View {
    var title: String
    @Binding var titleInput: String

    var body: some View {
        VStack {
            Text(title)
                .padding(10)

            TextField("Ingrese título", text: _titleInput.projectedValue)
                .textFieldStyle(.roundedBorder)
        }
    }
}

struct ContentView: View {
    @State private var tituloFinal: String = "Título inicial"
    @State private var title: String = ""

    var body: some View {
        VStack {
            // Acceso a la estructura subyacente
            ContentSubview(
                title: _tituloFinal.wrappedValue,
                titleInput: _title.projectedValue
            )

            Button {
                _tituloFinal.wrappedValue = _title.wrappedValue
            } label: {
                Text("Actualizar título")
            }
        }
    }
}

Binding manual

Aunque normalmente se usa Binding junto con @State, también es posible crear un Binding de forma manual mediante el inicializador init(get:set:), que recibe dos closures: uno para obtener el valor y otro para establecerlo. Esto resulta útil, por ejemplo, para previsualizar un componente que espera recibir un Binding.

struct ContentSubview: View {
    var title: String
    @Binding var titleInput: String

    var body: some View {
        VStack {
            Text(title)
                .padding(10)

            TextField("Ingrese título", text: _titleInput.projectedValue)
                .textFieldStyle(.roundedBorder)
        }
    }
}

Preview con Binding manual

#Preview("Manual binding") {
    var someTitle = ""

    // Binding manual que opera sobre `someTitle`
    let titleBinding = Binding(
        get: { someTitle },
        set: { newValue in
            someTitle = newValue
        }
    )

    ContentSubview(title: "Vista previa", titleInput: titleBinding)
}

Uso de Binding en vistas

struct ContentSubview: View {
    let title: String
    @Binding var titleInput: String

    var body: some View {
        VStack {
            Text(title)
            TextField("Título", text: $titleInput)
        }
    }
}

Creando un Binding manualmente

struct ContentView: View {
    @State private var title = ""

    var body: some View {
        ContentSubview(title: "Título inicial", titleInput: $title)
    }
}
#Preview("Manual binding") {
    @State var title = ""
    ContentSubview(title: "Título inicial", titleInput: $title)
}

Nota: En la vista previa (#Preview) no se muestra el botón de pantalla completa, por lo que los controles Enter fullscreen mode y Exit fullscreen mode se omiten.

Usando un Binding constante

A veces conviene pasar un valor inmutable al Binding. En este caso se usa el método estático .constant(_:):

#Preview("Binding constante") {
    ContentSubview(title: "Título inicial", titleInput: .constant("Algún título"))
}

Creando State desde #Preview

A partir de iOS 17/macOS 14, el macro @Previewable permite declarar un @State directamente dentro de un bloque #Preview, simplificando la escritura de vistas de prueba.

#Preview("@Previewable") {
    @Previewable @State var title = ""
    ContentSubview(title: "Título inicial", titleInput: $title)
}

Consejo: @Previewable simplifica la gestión del estado en vistas de prueba, ya que el estado se actualiza en tiempo real mientras editas el código.

Back to Blog

Related posts

Read more »

SwiftUI #21: Groups

¿Qué es un Group? Un Group agrupa varias vistas juntas para evitar el límite de sub‑vistas que tiene un Stack máximo 10 y permite aplicar estilos a varias vist...

SwiftUI #20: Prioridades

Introducción En SwiftUI, un Stack divide el espacio de forma equidistante entre sus vistas. Si las vistas no caben, asigna un tamaño fijo a los Image y reduce...