Programación en Swift y SwiftUI para iOS Developers

Cómo usar TabView en macOS con SwiftUI

Para un iOS Developer, el salto al desarrollo en macOS solía ser una travesía llena de NSView, Cocoa y paradigmas antiguos. Sin embargo, con la madurez de SwiftUI y Xcode, la línea entre ambos sistemas operativos se ha difuminado. El código es más compartible que nunca, pero la Experiencia de Usuario (UX) sigue siendo mundos aparte.

Uno de los componentes que más cambia de identidad al cruzar esta frontera es el humilde TabView. En iOS, es esa barra inferior omnipresente. En macOS, es un camaleón que puede ser una barra de herramientas, un selector de segmentos o la estructura base de una ventana de Preferencias.

En este tutorial de programación Swift, dejaremos de lado el iPhone por un momento. Nos centraremos exclusivamente en cómo implementar, personalizar y perfeccionar el TabView para macOS en SwiftUI, garantizando que tus aplicaciones se sientan nativas en el escritorio.


1. iOS vs. macOS

Antes de escribir una sola línea de código en Swift, debemos entender la filosofía de diseño.

  • En iOS, el TabView es el método principal de navegación de nivel superior. Está en la parte inferior porque es fácil de alcanzar con el pulgar.
  • En macOS, la ergonomía es visual, no táctil. No hay pulgares. Hay un cursor de ratón y una ventana redimensionable.

Al usar SwiftUI en el Mac, el TabView se comporta de manera diferente por defecto:

  1. Ubicación: Generalmente se renderiza en la parte superior o como un control segmentado.
  2. Propósito: Se usa comúnmente para paneles de configuración (Settings), alternar modos de vista o herramientas, más que para la navegación jerárquica principal.

Como iOS Developer, tu instinto será buscar la barra inferior. En este tutorial aprenderás a reprimir ese instinto y abrazar el diseño de escritorio.

2. Implementación Básica en Xcode

Vamos a abrir Xcode y crear un nuevo proyecto. Asegúrate de seleccionar “macOS” en la pestaña de plataforma. La sintaxis declarativa de SwiftUI es idéntica, pero el resultado visual es distinto.

import SwiftUI

struct MacContentView: View {
    var body: some View {
        TabView {
            Text("Panel de Control")
                .tabItem {
                    Label("General", systemImage: "gear")
                }
            
            Text("Usuarios")
                .tabItem {
                    Label("Cuentas", systemImage: "person.2")
                }
            
            Text("Red")
                .tabItem {
                    Label("Conectividad", systemImage: "network")
                }
        }
        .padding()
    }
}

¿Qué ocurre al ejecutar esto en Mac?
A diferencia de iOS, donde obtendrías una barra gris abajo, en macOS (dependiendo de la versión de macOS SDK), verás una interfaz de pestañas estándar similar a las preferencias del sistema antiguo.

La clave aquí es el modificador .padding(). En macOS, las vistas tienden a abrazar los bordes de la ventana. Un buen diseño en programación Swift para escritorio requiere que gestiones el “aire” y el espacio negativo manualmente.

3. Estilos de TabView: La clave de la personalización

El modificador .tabViewStyle() es donde ocurre la magia en macOS. Mientras que en iOS casi siempre usamos el .automatic (barra inferior) o .page, macOS ofrece variantes críticas para la interfaz de escritorio.

A. El estilo predeterminado (.automatic)

En macOS, el estilo automático intenta inferir el contexto. Si estás dentro de una ventana de Settings, adoptará el estilo de barra de herramientas superior característico de las preferencias de Apple.

B. Estilo Segmentado (Segmented Control)

Este es muy común para cambiar entre sub-vistas dentro de una pantalla más grande, no para la navegación global de la app.

TabView {
    // Vistas...
}
.tabViewStyle(.segmented)

4. El caso de uso rey: La Ventana de Ajustes (Settings)

Aquí es donde el TabView para macOS en SwiftUI brilla con luz propia. Todo iOS Developer que lanza una app en Mac debe crear una ventana de “Preferencias” o “Ajustes”. En AppKit esto era tedioso. En SwiftUI, es trivial.

En tu archivo principal de la App (YourApp.swift), no solo defines WindowGroup, sino también Settings.

@main
struct MiAppMac: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        
        // Esta escena crea automáticamente la entrada "Preferences..." 
        // en el menú de la aplicación.
        Settings {
            SettingsView()
        }
    }
}

struct SettingsView: View {
    var body: some View {
        TabView {
            GeneralSettingsView()
                .tabItem {
                    Label("General", systemImage: "gear")
                }
            
            AccountSettingsView()
                .tabItem {
                    Label("Cuentas", systemImage: "person.crop.circle")
                }
                
            AdvancedSettingsView()
                .tabItem {
                    Label("Avanzado", systemImage: "slider.horizontal.3")
                }
        }
        .frame(width: 450, height: 250) // Tamaño fijo típico de ventanas de ajustes
    }
}

Análisis Técnico:

  • Escena Settings: SwiftUI detecta esto y automáticamente coloca el ítem de menú “Settings” (o Preferencias) en la barra de menús de macOS, asignándole el atajo Cmd + ,.
  • Tamaño de Ventana: A diferencia de iOS, en Mac las ventanas flotan. Usar .frame(width:height:) en el TabView de ajustes es una buena práctica para evitar que la ventana sea redimensionable innecesariamente.

5. Navegación Programática y Atajos de Teclado

El usuario de Mac vive en el teclado. Un error común al portar apps de iOS es obligar al usuario a hacer clic en todo. Vamos a añadir navegación por teclado a nuestro TabView, una habilidad esencial en la programación Swift avanzada.

enum AppTab: String, CaseIterable, Identifiable {
    case dashboard, analytics, history
    var id: Self { self }
}

struct MacDashboard: View {
    @State private var selectedTab: AppTab = .dashboard
    
    var body: some View {
        TabView(selection: $selectedTab) {
            Text("Dashboard View")
                .tabItem { Label("Tablero", systemImage: "gauge") }
                .tag(AppTab.dashboard)
            
            Text("Analytics View")
                .tabItem { Label("Datos", systemImage: "chart.bar") }
                .tag(AppTab.analytics)
        }
        // Atajos de teclado globales
        .background {
            Button("Ir a Dashboard") { selectedTab = .dashboard }
                .keyboardShortcut("1", modifiers: .command)
                .hidden() // Truco para añadir atajos sin botón visible
            
            Button("Ir a Analytics") { selectedTab = .analytics }
                .keyboardShortcut("2", modifiers: .command)
                .hidden()
        }
    }
}

Al añadir .keyboardShortcut, permites que el usuario pulse Cmd+1 o Cmd+2 para cambiar de pestaña. Este es el tipo de detalle que diferencia a una app portada de una aplicación Mac nativa de primera clase.

6. Personalización Avanzada y “Look and Feel”

Si necesitas un control total (por ejemplo, pestañas laterales personalizadas o una barra superior propia), a menudo la solución no es personalizar el TabView nativo, sino ocultarlo y crear uno propio, tal como se hace en iOS para diseños muy específicos.

// Ocultar las pestañas nativas para hacer una UI propia
TabView(selection: $selection) {
    // Vistas
}
.tabsStyle(.hidden) // (Conceptual, requiere trucos en versiones antiguas)

7. Estrategia Cross-Platform para el iOS Developer

Como desarrollador, probablemente quieras que tu código sirva para iPhone, iPad y Mac. ¿Cómo gestionas el TabView?

El patrón recomendado es crear una “Vista de Navegación Raíz” que decida qué contenedor usar basándose en el sistema operativo usando directivas de compilador #if os(macOS).

struct RootNavigator: View {
    #if os(iOS)
    @Environment(\.horizontalSizeClass) var sizeClass
    #endif

    var body: some View {
        #if os(macOS)
        // En Mac: Sidebar + Detail o Pestañas superiores
        MacSidebarView()
        #else
        // En iOS
        if sizeClass == .compact {
            // iPhone: TabBar inferior
            AppTabView()
        } else {
            // iPad: Sidebar
            iPadSidebarView()
        }
        #endif
    }
}

8. Problemas Comunes y Rendimiento

Al trabajar con TabView para macOS en SwiftUI, te encontrarás con retos específicos, como la gestión del foco.

Gestión del Foco (Focus State)

Cuando cambias de pestaña en Mac, el “foco” (dónde está el cursor del teclado) puede perderse. Usa @FocusState en SwiftUI para asegurar que, al cambiar a la pestaña de “Búsqueda”, el campo de texto obtenga el foco automáticamente.

@FocusState private var isSearchFocused: Bool

// Al cambiar a la pestaña search:
.onChange(of: selectedTab) { newValue in
    if newValue == .search {
        isSearchFocused = true
    }
}

Conclusión

El TabView en macOS es un componente engañosamente simple. Para un iOS Developer, representa el primer desafío real de adaptación: dejar atrás la navegación táctil vertical y abrazar la navegación horizontal de puntero.

Al dominar el TabView para macOS en SwiftUI, no solo estás portando tu app; estás respetando la plataforma y a sus usuarios. Estás utilizando herramientas nativas como la ventana de Settings, atajos de teclado y estilos de control segmentado que hacen que tu aplicación se sienta “en casa”.

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

Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Article

NavigationStack con TabView en SwiftUI

Next Article

Tab Bar curvo en SwiftUI

Related Posts