DEV Community

David Goyes
David Goyes

Posted on

SwiftUI #2: Hola-mundo

A continuación presento una vista básica en SwiftUI que tiene una imagen, un texto y un botón. Actualmente no hay ningún cambio cuando se presiona el botón.

struct Example: View {
  var body: some View {
    VStack {
      Image(systemName: "pencil")
        .resizable()
        .frame(width: 200, height: 200)
        .foregroundStyle(.tint)
        .tint(Color.red)
      Text("Hola mundo")
        .padding()
      Button("Presióname") {
        // Acción
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Lógica condicional por variable de instancia

Es común que nuestra vista cambie de acuerdo a un cambio de su estado interno. Por esta razón, modifiquémosla para presentar un texto diferente dependiendo del valor de la variable de instancia saludo.

struct Example: View {
  var saludo = false
  var body: some View {
    VStack {
      Image(systemName: "pencil")
        .resizable()
        .frame(width: 200, height: 200)
        .foregroundStyle(.tint)
        .tint(Color.red)
      // Se presenta un texto diferente, dependiendo de la variable saludo.
      if saludo {
        Text("Hola mundo")
          .padding()
      } else {
        Text("Adiós")
          .padding()
      }
      Button("Presióname") {
        // Acción
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Vamos a cambiar el estado de saludoa verdadero cuando se presione el botón. Esto es:

Button("Presióname") {
  // ERROR: Cannot assign to property: 'self' is immutable
  saludo = true
}
Enter fullscreen mode Exit fullscreen mode

Por desgracia, la implementación anterior busca modificar el estado de la estructura, lo cual no es permitido. Para ello, se tiene que mover el estado a otro tipo de dato plantilla llamado State<X>, que se logra usando la etiqueta @State.

struct Example: View {
  @State var saludo = false
  var body: some View {
    VStack {
      Image(systemName: "pencil")
        .resizable()
        .frame(width: 200, height: 200)
        .foregroundStyle(.tint)
        .tint(Color.red)
      if saludo {
        Text("Hola mundo")
          .padding()
      } else {
        Text("Adiós")
          .padding()
      }
      Button("Presióname") {
        saludo = true
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Implementación reactiva

El código anterior era pasivo, puesto que modificaba saludo desde el botón y lo leía para pintar un texto diferente. No obstante, @State tiene un @Binding que funciona como observador - No es un Publisher de Combine, pero se parece en el sentido de que reacciona a cambios.
Cada vez que la variable saludo se vuelva true quiero presentar una alerta con título "ATENCIÓN" y mensaje "Esto es un saludo". Para ello aplico el modificador alert(_:isPresented:presenting:actions:message:) que espera recibir un título, un Binding booleano, una lista de acciones y un mensaje. Si la lista de acciones está vacía se muestra un botón por defecto con título "OK". Cada botón puede tener una acción diferente pero todos por defecto cambian el estado del Binding isPresented a false.

struct Example: View {
  @State var saludo = false
  var body: some View {
    VStack {
      Image(systemName: "pencil")
        .resizable()
        .frame(width: 200, height: 200)
        .foregroundStyle(.tint)
        .tint(Color.red)
      if saludo {
        Text("Hola mundo")
            .padding()
      } else {
        Text("Adiós")
            .padding()
      }
      Button("Presióname") {
        saludo = true
      }
    }
    .alert("ATENCION", isPresented: $saludo) {
      // Si se queda vacío muestra un botón de "OK"
    } message: {
      Text("Esto es un saludo")
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

En el caso de que se quiera definir una acción específica a la alerta, hay que agregar un botón para ello así:

.alert("ATENCION", isPresented: $saludo) {
  Button("Cerrar") {
      print("Esta es la acción de cerrar")
  }
} message: {
  Text("Esto es un saludo")
}
Enter fullscreen mode Exit fullscreen mode

Diferencia entre variable cruda y Binding

Notar que el botón "Presióname" hace uso de saludo, cambiado su estado a true. En cambio, la alerta hace uso de saludo a través del Binding: $saludo que se denomina "projected-value". En el primer caso, la referencia a la variable es de solo lectura mientras que, en el segundo caso, la referencia es de lectura y escritura.
Se dice que @State es una fuente de verdad que almacena un dato. Binding es un property-wrapper que puede leer y escribir el valor de una fuente de verdad.

SwiftUI como framework

Aunque SwftUI provee una nueva forma de definir una interfaz, igual sigue usando los elementos existentes de la plataforma. Un botón de SwiftUI se convierte en un UIButton en iOS y en un NSButton en macOS.

Modificador vs Vista

En el cuerpo de la vista Example se usaron componentes visuales primitivos como Text, Button, Image, y VStack. Por otro lado, en ocasiones, debajo de estas vistas aparecieron unos modificadores como .padding().resizable().
Veremos más de esto en el siguiente artículo.

Top comments (0)