DEV Community

GoyesDev
GoyesDev

Posted on

[SUI] Modificando valores en un contenedor personalizado

En ocasiones no queremos construir un componente hoja, sino un contenedor que almacene a otros subcomponentes.

En el siguiente ejemplo, Container es una vista genérica que espera recibir content de tipo @ViewBuilder que retorna Content, que conforma View. Observar:

  1. Se debe definir la distribución a pintar dentro de Container, en este caso: con HStack.
  2. Se define un conjunto de subvistas dentro de un "closure" en ContentView.
struct Container<Content: View>: View {
  @ViewBuilder var content: Content
  var body: some View {
    HStack {
      content
    }
  }
}

struct ContentView: View {
  var body: some View {
    Container {
      Text("Uno")
      Text("Dos")
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Manipulando los items individualmente desde el contenedor

Para acceder a los items en Container se usa ForEach.init(subviews:content:) o Group.init(subviews:transform:).

La primera aproximación (ForEach) permite recorrer todos los elementos, uno a uno, mientras que la segunda (Group) da acceso a todos los ítems -Lo cual es útil si se quiere hacer una operación sobre la colección antes de operarla con ForEach.

struct Container<Content: View>: View {
  @ViewBuilder var content: Content
  var body: some View {
    HStack {
      ForEach(subviews: content) { subview in
        subview
          .padding()
          .background(.red.opacity(0.5))
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Inyectando un valor de configuración desde una subvista hacia el contenedor

Algunas veces el contenedor no tiene información suficiente para configurar a la subvista. Por esta razón, la subvista debe hacer una "inyección reversa". Para ello:

  1. Se agrega una nueva llave x en ContainerValues en una extensión por medio de una propiedad, con un valor por defecto, marcada como @Entry.
  2. Se inyecta el valor en la subvista con containerValue(_:_:) usando la llave x definida en el paso anterior.
  3. Luego, se accede a la llave x en el atributo containerValues de tipo ContainerValues de la subvista en cuestión.
  4. Con base en x, se hace la modificación sobre la subvista que haga falta.

Por ejemplo:

extension ContainerValues {
  // ⚠️ 1. Se crea la nueva llave con valor por defecto
  @Entry var valida = false
}

struct Container<Content: View>: View {
  @ViewBuilder var content: Content
  var body: some View {
    HStack {
      ForEach(subviews: content) { subview in
  // ⚠️ 3. Se extrae el valor del containerValues
        let valida = subview.containerValues.valida
        subview
          .padding()
          .background(valida ? .green.opacity(0.5) : .red.opacity(0.5)) // ⚠️ 4. Se hace la modificación necesaria
      }
    }
  }
}

struct ContentView: View {
  var body: some View {
    Container {
  // ⚠️ 2. Se inyecta el valor en la subvista
      Text("Uno")
        .containerValue(\.valida, true)

  // ⚠️ 2. Aquí se toma el valor por defecto
      Text("Dos")

  // ⚠️ 2. Aquí se vuelve a inyectar
      Text("Tres")
        .containerValue(\.valida, false)
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)