Si eres un iOS Developer buscando llevar la interfaz de tus aplicaciones al siguiente nivel, probablemente te hayas topado con las limitaciones del TabView nativo de Apple. Aunque es funcional y respeta las guías de diseño de la plataforma, en el mundo del desarrollo moderno a menudo necesitamos interfaces únicas que destaquen. Aquí es donde entra en juego el Tab Bar personalizado en SwiftUI.
En este tutorial, exploraremos a fondo qué es, por qué deberías considerar construir uno desde cero, y te guiaré paso a paso mediante programación Swift para crear un sistema de navegación inferior totalmente a medida. Además, diseñaremos este componente para que sea compatible no solo en iOS, sino también en macOS y watchOS, aprovechando la versatilidad que nos ofrece Swift, Xcode y SwiftUI.
¿Qué es un Tab Bar Personalizado y por qué lo necesitas?
En el ecosistema de Apple, un “Tab Bar” (barra de pestañas) es el elemento de navegación principal que suele ubicarse en la parte inferior de la pantalla en iOS. Permite a los usuarios alternar rápidamente entre diferentes secciones de una aplicación.
El componente nativo de SwiftUI para esto es TabView. Sin embargo, si has intentado cambiar su altura, añadir un botón flotante central que sobresalga (al estilo de Instagram o Twitter), o aplicar animaciones complejas al cambiar de pestaña, te habrás dado cuenta de que el sistema nativo es bastante rígido.
Crear un Tab Bar personalizado en SwiftUI significa prescindir de la barra nativa (ocultándola) o usar un ZStack para superponer tu propia vista de navegación sobre el contenido. Esto te da control absoluto sobre:
- Animaciones y transiciones.
- Formas geométricas (barras flotantes, esquinas redondeadas personalizadas).
- Comportamiento responsivo en múltiples plataformas.
Requisitos Previos
Para seguir este tutorial de programación Swift, asegúrate de contar con:
- Xcode 14 o superior (recomendado Xcode 15+ para las últimas novedades).
- Conocimientos intermedios de SwiftUI (entender
ZStack,HStack,@Statey@Binding). - Un proyecto multi-plataforma creado en Xcode (seleccionando iOS, macOS y watchOS si deseas probar todo el código).
Paso 1: Definiendo el Modelo de Datos (Las Pestañas)
Todo buen código en Swift comienza con un modelo de datos sólido. En lugar de usar cadenas de texto sueltas o índices enteros para saber en qué pestaña estamos, utilizaremos un Enum. Esto hace que nuestro código sea seguro, predecible y fácil de escalar.
Crea un nuevo archivo llamado TabModel.swift en tu proyecto de Xcode:
import Foundation
// Definimos los casos de nuestro Tab Bar
enum Tab: String, CaseIterable {
case inicio = "house.fill"
case buscar = "magnifyingglass"
case notificaciones = "bell.fill"
case perfil = "person.fill"
// Título opcional para cada pestaña
var title: String {
switch self {
case .inicio: return "Inicio"
case .buscar: return "Buscar"
case .notificaciones: return "Avisos"
case .perfil: return "Perfil"
}
}
}
Usamos los nombres de los íconos de SF Symbols como el valor crudo (RawValue) del enum. Esto nos ahorrará tiempo al construir la interfaz en SwiftUI.
Paso 2: Construyendo la Vista del Tab Bar Personalizado
Ahora vamos a crear el componente visual. Este será una vista independiente que recibirá la pestaña actualmente seleccionada a través de un @Binding, lo que permitirá que la vista padre actualice su contenido cuando el usuario toque un botón.
Crea un archivo llamado CustomTabBarView.swift:
import SwiftUI
struct CustomTabBarView: View {
@Binding var currentTab: Tab
// Detectamos el sistema operativo para ajustar el diseño
#if os(iOS) || os(macOS)
var iconSize: CGFloat = 24
#elseif os(watchOS)
var iconSize: CGFloat = 18
#endif
var body: some View {
HStack(spacing: 0) {
ForEach(Tab.allCases, id: \.rawValue) { tab in
Button {
withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {
currentTab = tab
}
} label: {
VStack(spacing: 4) {
Image(systemName: tab.rawValue)
.font(.system(size: iconSize, weight: .semibold))
// Animación de escala cuando está seleccionado
.scaleEffect(currentTab == tab ? 1.2 : 1.0)
#if !os(watchOS)
// Ocultamos el texto en watchOS para ahorrar espacio
Text(tab.title)
.font(.caption2)
.fontWeight(currentTab == tab ? .bold : .regular)
#endif
}
.foregroundColor(currentTab == tab ? .blue : .gray)
.frame(maxWidth: .infinity)
// Añadimos un fondo transparente para hacer toda el área clickeable
.contentShape(Rectangle())
}
.buttonStyle(PlainButtonStyle()) // Evita el estilo por defecto en macOS
}
}
.padding(.top, 10)
.padding(.bottom, bottomPadding)
.background(
// Fondo personalizado con blur y bordes redondeados
RoundedRectangle(cornerRadius: 25, style: .continuous)
.fill(Material.bar)
.shadow(color: .black.opacity(0.1), radius: 10, x: 0, y: -5)
)
.padding(.horizontal)
}
// Helper para manejar el padding inferior según la plataforma
private var bottomPadding: CGFloat {
#if os(iOS)
return 15 // Espacio para el Home Indicator en iPhones modernos
#else
return 10
#endif
}
}
Análisis del Código para el iOS Developer
- Animaciones: Usamos
withAnimation(.spring(...))dentro de la acción del botón. Esto es una técnica fundamental de programación Swift en UI para dar una sensación orgánica y fluida al cambiar de pestaña. - Adaptabilidad: Fíjate en el uso de las directivas
#if os(iOS). Como iOS Developer, es vital recordar que SwiftUI es multiplataforma, pero las pantallas de Apple Watch y Mac tienen densidades y patrones de uso distintos. En watchOS reducimos el tamaño del ícono y eliminamos el texto para evitar saturar la pequeña pantalla. - Material Design de Apple: El uso de
Material.baren el fondo proporciona ese efecto translúcido (efecto cristal) tan característico de los sistemas operativos de Apple.
Paso 3: Integrando el Tab Bar en la Vista Principal
Con nuestro Tab Bar personalizado en SwiftUI ya diseñado, necesitamos un lugar donde usarlo. La estrategia aquí no es usar el TabView estándar y ocultar su barra (aunque es posible, suele dar problemas de “safe area”). La mejor práctica en SwiftUI para un control total es usar un ZStack y manejar el enrutamiento manualmente.
Crea (o modifica) el archivo ContentView.swift:
import SwiftUI
struct ContentView: View {
// Estado inicial de la aplicación
@State private var selectedTab: Tab = .inicio
// Ocultamos el fondo nativo en macOS si es necesario
init() {
#if os(iOS)
UITabBar.appearance().isHidden = true
#endif
}
var body: some View {
ZStack(alignment: .bottom) {
// 1. ÁREA DE CONTENIDO PRINCIPAL
Group {
switch selectedTab {
case .inicio:
HomeView()
case .buscar:
SearchView()
case .notificaciones:
NotificationsView()
case .perfil:
ProfileView()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
// Aseguramos que el contenido no quede oculto detrás de nuestra barra flotante
.padding(.bottom, 80)
// 2. NUESTRO TAB BAR PERSONALIZADO
CustomTabBarView(currentTab: $selectedTab)
// Usamos un padding condicional para que flote un poco
.padding(.bottom, 10)
}
.ignoresSafeArea(.keyboard, edges: .bottom) // Evita que el teclado suba el Tab Bar
}
}
// Vistas de ejemplo para llenar el contenido
struct HomeView: View {
var body: some View {
ZStack {
Color.blue.opacity(0.1).ignoresSafeArea()
Text("Pantalla de Inicio").font(.largeTitle).bold()
}
}
}
struct SearchView: View {
var body: some View {
ZStack {
Color.green.opacity(0.1).ignoresSafeArea()
Text("Pantalla de Búsqueda").font(.largeTitle).bold()
}
}
}
struct NotificationsView: View {
var body: some View {
ZStack {
Color.orange.opacity(0.1).ignoresSafeArea()
Text("Pantalla de Notificaciones").font(.largeTitle).bold()
}
}
}
struct ProfileView: View {
var body: some View {
ZStack {
Color.purple.opacity(0.1).ignoresSafeArea()
Text("Pantalla de Perfil").font(.largeTitle).bold()
}
}
}
La Lógica detrás del ZStack
Almacenar las vistas dentro de un Group controlado por un switch basado en el selectedTab es una de las arquitecturas más limpias en la programación Swift declarativa. Cuando el @State cambia en CustomTabBarView, ContentView se reevalúa, el switch cae en un caso diferente, y SwiftUI renderiza la nueva vista de forma ultra-eficiente.
El modificador .ignoresSafeArea(.keyboard, edges: .bottom) es un salvavidas crítico para cualquier iOS Developer. Evita un bug visual muy común donde, al abrir un campo de texto en alguna de tus pantallas, el teclado empuja el Tab Bar personalizado hacia el centro de la pantalla.
Paso 4: Adaptación Específica para macOS y watchOS
La belleza de usar Xcode y SwiftUI radica en compartir código. Sin embargo, un buen desarrollador sabe que “funcionar” no es lo mismo que “sentirse nativo”.
Consideraciones para macOS
En aplicaciones de escritorio, un Tab Bar flotante en la parte inferior puede parecer una adaptación directa de móvil. En macOS, los usuarios están más acostumbrados a las Sidebars (barras laterales). Si bien el componente que hicimos funciona perfectamente en Mac, podrías usar directivas de compilación para renderizar un HStack en macOS en lugar de un ZStack:
// Ejemplo conceptual para ContentView adaptado a Mac
#if os(macOS)
HStack {
// Convertimos nuestro Tab Bar horizontal en uno vertical para Mac
CustomSidebarView(currentTab: $selectedTab)
.frame(width: 200)
// Contenido Principal
MainContentView(selectedTab: selectedTab)
}
#else
// Código original del ZStack para iOS y watchOS
#endif
Consideraciones para watchOS
Para el Apple Watch, el espacio es crítico. Nuestro código de CustomTabBarView ya elimina el texto y reduce el ícono. Sin embargo, en watchOS 10+, Apple introdujo nuevas formas de navegación vertical. Si decides mantener este Tab Bar inferior en el reloj, asegúrate de colocarlo dentro de un ScrollView si tu vista principal es muy alta, o fijarlo al fondo de la pantalla cuidando de no tapar elementos clave.
Conclusión
Dominar la creación de un Tab Bar personalizado en SwiftUI te separa de ser un principiante a convertirte en un iOS Developer avanzado. Has aprendido a saltarte las limitaciones del framework nativo utilizando programación Swift limpia y escalable.
Al utilizar enums para la gestión del estado, ZStack para el enrutamiento y macros de compilación (#if os) para la compatibilidad multiplataforma, has creado una arquitectura robusta en Xcode que puede soportar desde aplicaciones simples hasta proyectos empresariales complejos.
Si tienes cualquier duda sobre este artículo, contacta conmigo y estaré encantado de ayudarte . Puedes contactar conmigo en mi perfil de X o en mi perfil de Instagram.










