DEV Community

David Goyes
David Goyes

Posted on • Edited on

SwiftUI #4: Contenedores básicos: VStack, HStack, ZStack, Group

Pintemos una vista con un Text y un Image, como se muestra a continuación:

var body: some View {
  Image(systemName: "table")
    .resizable()
    .frame(width: size, height: size)
    .overlay {
      Circle().stroke(Color.gray, lineWidth: 1)
    }
    .background(Color(white: 0.9))
    .foregroundStyle(.red)
    .clipShape(Circle())

  Text("Welcome to Kuchi")
    .font(.system(size: 30))
    .bold()
    .foregroundColor(.red)
    .lineLimit(2)
    .multilineTextAlignment(.center)
}
Enter fullscreen mode Exit fullscreen mode

Esta no es la forma correcta de añadir varias subvistas a una vista. Con algunas excepciones, la propiedad body de una vista espera solo una subvista.

En SwiftUI 1.0, el código de arriba habría provocado un error de compilación. Ahora compila e incluso funciona en el simulador: todas las subvistas están apiladas de forma vertical.

Si se quiere empaquetar más de una subvista en una vista, se debe usar un contenedor. El más usado es el (-)Stack, que viene siendo la versión análoga del UIStackView de UIKit.

(-)Stack

En SwiftUI podemos agrupar elementos de una pila de forma horizontal, vertical, o uno sobre otro. Para esto se usan los componentes HStack, VStack y ZStack respectivamente, que reciben un conjunto de componentes visuales en una función @ViewBuilder, y los pinta a todos de una sola vez, independiente de que esté en o fuera de la pantalla.
Se sugiere usar este tipo de pilas de vistas cuando se tenga un número pequeño de subvistas o no se quiera retrasar el pintado con la versión "perezosa" de los contenedores.
Las pilas de vistas (sin importar la dirección) reciben por constructor:

  • alignment: La guía de alineación de subvistas en el stack. Para el VStack será de tipo HorizontalAlignment, para el HStack será VerticalAlignment y para ZStack será Alignment.
  • spacing: La distancia entre subvistas adyacentes o nil si se quiere que el stack ponga la distancia por defecto entre un par de subvistas.
  • content: Un ViewBuilder que crea el contenido del stack.

VStack

Distribuye los componentes en dirección vertical.

var body: some View {
  VStack(
    alignment: .leading,
    spacing: 10
  ) {
    ForEach(
      1...10,
      id: \.self
    ) {
      Text("Item \($0)")
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

La propiedad alignment sirve para alinear las vistas dentro del Stack. Por defecto tiene el valor .center, sin embargo, podríamos alinear los componentes hacia uno de los extremos de la fila con .leading y .trailing.

HStack

Distribuye los componentes en dirección horizontal.

private struct VerticalAlignmentGallery: View {
  var body: some View {
    VStack(spacing: 30) {
      row(alignment: .top, text: "Top")
      row(alignment: .center, text: "Center")
      row(alignment: .bottom, text: "Bottom")
      row(alignment: .firstTextBaseline, text: "First Text Baseline")
      row(alignment: .lastTextBaseline, text: "Last Text Baseline")
    }
  }
  private func row(alignment: VerticalAlignment, text: String) -> some View {
    HStack(alignment: alignment, spacing: 0) {
      Color.red.frame(height: 1)
      Text(text).font(.title).border(.gray)
      Color.red.frame(height: 1)
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Parecido al caso del VStack, la propiedad alignment sirve para alinear las vistas dentro del Stack. Por defecto tiene el valor .center, sin embargo, podríamos alinear los componentes hacia uno de los extremos de la columna con .top y .bottom.

ZStack

Partiendo de que la dirección Z es perpendicular al plano de la pantalla, el ZStack apila elementos uno sobre otro. Items "arriba" del closure aparecerán "más abajo" en el stack view. Sería como poner la primera vista sobre la superficie de la pantalla, luego apilar la siguiente vista encima y así sucesivamente.

Group

Group es otro contenedor que no pinta nada en pantalla. Es útil cuando se necesita envolver código que es más complicado que una sola vista.

Modificadores de contenedores

Cuando se tenga una vista contenedora y se quiera aplicar uno o más modificadores a todas las subvistas, se puede aplicar los modificadores al contenedor.

Por ejemplo, en lugar de escribir:

VStack {
  Text("Welcome")
    .font(.system(size: 30))
    .bold()
    .foregroundStyle(.red)
    .lineLimit(2)
    .multilineTextAlignment(.center)

  Text("to Kuchi")
    .font(.system(size: 30))
    .bold()
    .foregroundStyle(.red)
    .lineLimit(2)
    .multilineTextAlignment(.center)
}
Enter fullscreen mode Exit fullscreen mode

Se puede escribir:

VStack {
  Text("Welcome")
    .font(.system(size: 30))
    .bold()
  Text("to Kuchi")
    .font(.system(size: 30))
    .bold()
}
.foregroundStyle(.red)
.lineLimit(2)
.multilineTextAlignment(.center)
Enter fullscreen mode Exit fullscreen mode

Teniendo en cuenta que .foregroundStyle(.red), .lineLimit(2) y .multilineTextAlignment(.center).

En versiones anterior de SwiftUI font(.system(size: 30)) y .bold() no podían ser aplicados al contenedor porque eran modificadores de Text. Sin embargo, actualmente pueden ser aplicados al contenedor, quien propaga la modificación hacia las instancias de Text contenidas.

El código anterior quedaría de esta forma:

VStack {
  Text("Welcome")
  Text("to Kuchi")
}
.font(.system(size: 30))
.bold()
.foregroundStyle(.red)
.lineLimit(2)
.multilineTextAlignment(.center)
Enter fullscreen mode Exit fullscreen mode

Aunque aplicar un modificador al contenedor puede propagar efectos en las subvistas, este puede ser sobre-escrito en cada subvista. Por ejemplo, aunque se definió la fuente en 30 en el contenedor, puedo cambiar la fuente en uno de los Text y esto va a tener más prioridad:

VStack {
  Text("Welcome")
    .font(.headline)
  Text("to Kuchi")
}
.font(.system(size: 30))
.bold()
.foregroundStyle(.red)
.lineLimit(2)
.multilineTextAlignment(.center)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)