SwiftUI: componente TabView

Qué es Tab View

El componente TabView es un contenedor de vistas que permite intercambiar la vista hija que se muestra en cada momento. Es un componente equivalente a UITabBarController de UIKit. A partir de iOS 14 también se puede comportar como un UIPageViewController de UIKit.

Aquí podéis consultar la documentación oficial

Para crear el componente solo necesitaremos embeber en su ViewBuilder las vistas que podrá mostrar y el propio componente nos proporcionará una barra inferior para poder intercambiar la vista que se muestra en cada momento.

TabView {
    Text("First Screen")
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
        .background(Color.yellow.opacity(0.3))
        .tabItem {
            Image(systemName: "1.circle.fill")
            Text("First")
        }
    Text("Second Screen")
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
        .background(Color.red.opacity(0.3))
        .tabItem {
            Image(systemName: "2.circle.fill")
            Text("Second")
        }
}

La barra inferior contendrá tantos botones como vistas embebidas contenga y permitirá mostrar un texto e icono por cada una de las opciones a través del modificador tabItem.

Cómo seleccionar una página ‘programáticamente’

El ‘inicializador’ de TabView tiene un parámetro selection que permite indicar la vista del TabView debe presentarse. Este parámetro hace referencia al tag de la vista a presentar, por lo que todas las vistas que se incluyan en el TabView deberán tener definido el tag. El tag es un modificador del protocolo View y lo asignaremos de la siguiente forma:

Text("Hello, world!")
    .tag(0)

El valor de tag es de tipo Hashable, que implementa String o Int, entre otros.

Por ejemplo, vamos a definir un TabView con dos botones que al pulsarlos se naveguen entre las dos pantallas, sin necesidad de pulsar las opciones del TabView. Para ello, definiremos los tag de cada uno de los botones, declararemos una variable de estado y la asociaremos a la propiedad selection del TabView. De esta forma al pulsar cada botón se cambiará la variable de estado selection para indicar el tag que se debe mostrar.

struct ContentView: View {
    @State private var selection: Int = 0
    
    var body: some View {
        TabView(selection: $selection) {
            Group {
                Button("Go to Second Screen") {
                    selection = 1
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
                .background(Color.yellow.opacity(0.3))
                .tabItem {
                    Image(systemName: "1.circle.fill")
                    Text("First")
                }
                .tag(0)
                
                Button("Go to First Screen") {
                    selection = 0
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
                .background(Color.red.opacity(0.3))
                .tabItem {
                    Image(systemName: "2.circle.fill")
                    Text("Second")
                }
                .tag(1)
            }
        }
    }
}

Modificadores comunes para Tabview

El componente TabView comparte los mismos métodos de personalización que el componente View y pueden ser consultados en el siguiente enlace.

accentColor

Permite modificar el color del botón que indica que pantalla está seleccionada.

TabView {
    Group {
        Text("First Screen")
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
            .background(Color.yellow.opacity(0.3))
            .tabItem {
                Image(systemName: "1.circle.fill")
                Text("First")
            }
        Text("Second Screen")
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
            .background(Color.red.opacity(0.3))
            .tabItem {
                Image(systemName: "2.circle.fill")
                Text("Second")
            }
    }
}
.accentColor(.orange)

tabItem

Permite indicar a las vistas hijas la imagen y el texto que deberá mostrarse cuando se incluya en un componente TabView.

TabView {
    Group {
        Text("First Screen")
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
            .background(Color.yellow.opacity(0.3))
            .tabItem {
                Image(systemName: "cart.fill")
                Text("First")
            }
        Text("Second Screen")
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
            .background(Color.red.opacity(0.3))
            .tabItem {
                Image(systemName: "creditcard")
                Text("Second")
            }
    }
}

El modificador recibe como parámetro un ViewBuilder que permite añadir una Image y un Text.

Para el ejemplo se han usados las imágenes SF Symbols pre-cargadas en el sistema.

tabViewStyle (iOS14)

Permite alternar el estilo de presentación de la vistas del componente TabView. Por defecto la SDK trae dos implementaciones para iOS que implementan el protocolo TabViewStyle:

  • DefaultTabViewStyle. Se corresponde al modo de presentación correspondiente a un UITabBarController de UIKit.
  • PageTabViewStyle. Se corresponde al modo de presentación correspondiente a un UIPageViewController de UIKit.
struct TabViewStyle4View: View {
    var body: some View {
        TabView {
            Group {
                Text("First Screen")
                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
                    .background(Color.blue.opacity(0.3))
                    .tabItem {
                        Image(systemName: "cart.fill")
                        Text("First")
                    }
                Text("Second Screen")
                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
                    .background(Color.red.opacity(0.3))
                    .tabItem {
                        Image(systemName: "creditcard")
                        Text("Second")
                    }
            }
        }
        .tabViewStyle(PageTabViewStyle())
    }
}

Cómo cambiar el color de la barra del TabView

Actualmente no hay forma de modificar el color de la barra inferior del TabView con SwiftUI, lo cual es necesario en algunas ocasiones por temas de diseño.

Existe una forma con las apis de UIKit, pero es un cambio global y afectaría a todos los componentes TabView del proyecto. Sería usando la api de appearence de la siguiente forma:

UITabBar.appearance().barTintColor = UIColor(red: 180/255, green: 207/255, blue: 227/255, alpha: 1)

Este código lo podemos invocar en el método init de nuestra vista o en el método onAppear (y podemos restablecerlo en el método onDisappear para minimizar los fallos).

struct ContentView: View {
    
    private var lastBarTintColor: UIColor? = UITabBar.appearance().barTintColor
    
    var body: some View {
        TabView {
            Group {
                Text("First Screen")
                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
                    .background(Color.yellow.opacity(0.3))
                    .tabItem {
                        Image(systemName: "1.circle.fill")
                        Text("First")
                    }
                Text("Second Screen")
                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
                    .background(Color.red.opacity(0.3))
                    .tabItem {
                        Image(systemName: "2.circle.fill")
                        Text("Second")
                    }
            }
        }
        .onAppear {
            UITabBar.appearance().barTintColor = UIColor(red: 180/255, green: 207/255, blue: 227/255, alpha: 1)
        }
        .onDisappear {
            UITabBar.appearance().barTintColor = lastBarTintColor
        }
    }
}

IMPORTANTE: recordad que esto es un cambio global y puede provocar fallos en otros puntos de la aplicación si no se controla correctamente.

Ejemplo

Puedes encontrar este ejemplo en https://github.com/SDOSLabs/SwiftUI-Test bajo el apartado TabView.

Rafael Fernández,
iOS Tech Lider