Programación en Swift y SwiftUI para iOS Developers

Qué es y cómo usar Label en SwiftUI

En el vasto ecosistema de SwiftUI, existen componentes que a primera vista parecen redundantes. ¿Por qué necesitamos un componente Label si ya tenemos Text e Image, y podemos combinarlos fácilmente dentro de un HStack?

Esta es una pregunta que muchos desarrolladores se hacen al empezar. La respuesta corta es: Semántica y Adaptabilidad.

Label no es solo un contenedor visual; es una estructura semántica que describe una pieza de interfaz de usuario que consta de un título y un icono. Al usar Label, le estás dando al sistema operativo la libertad de renderizar ese contenido de la forma más apropiada según el contexto (una lista, una barra de herramientas, un menú contextual o un lector de pantalla).

En este tutorial, desglosaremos cada aspecto de Label en SwiftUI, desde su implementación más básica hasta la creación de sistemas de estilos personalizados y complejos para iOS, macOS y watchOS.


1. ¿Qué es Label y por qué deberías usarlo?

Label es una vista estándar introducida en SwiftUI 2.0 (iOS 14). Su propósito es unificar un texto descriptivo y una imagen representativa en un solo elemento cohesivo.

El problema del HStack

Antes de Label, solíamos hacer esto:

HStack {
    Image(systemName: "folder")
    Text("Mis Archivos")
}

Si bien esto funciona visualmente en el canvas, tiene desventajas:

  1. Accesibilidad: VoiceOver lee “Imagen, carpeta” y luego “Mis Archivos”. No están vinculados intrínsecamente.
  2. Contexto: Si pones este HStack dentro de una barra de herramientas (ToolbarItem), es posible que el sistema no sepa cómo formatearlo correctamente.
  3. Menús: En los menús contextuales de iOS, un HStack no alinea el icono a la derecha automáticamente como lo hace el sistema nativo.

La solución con Label

Label("Mis Archivos", systemImage: "folder")

Con esta simple línea, ganas alineación automática de Dynamic Type (el texto y el icono escalan juntos), soporte nativo en Menús y Listas, y una mejor experiencia de accesibilidad.


2. Inicializadores Básicos: Creando tu primera Etiqueta

SwiftUI ofrece varias formas de instanciar un Label, dependiendo de la fuente de tus datos.

A. Texto y SF Symbols (El más común)

La forma más rápida de prototipar y construir interfaces nativas es usando SF Symbols.

Label("Configuración", systemImage: "gear")

Nota: El icono y el texto se alinean automáticamente en la línea base (baseline).

B. Texto e Imagen de Assets

Si tienes un icono personalizado en tu catálogo de Assets.xcassets:

Label("Mi Perfil", image: "icono_usuario_personalizado")

Importante: Asegúrate de que tu imagen en los Assets esté configurada como “Render As: Template Image” si quieres que SwiftUI pueda cambiarle el color dinámicamente.

C. El Inicializador Flexible (Closures)

A veces, el título no es solo un String, o la imagen no es solo un icono estático. Quizás el icono es una vista compleja o un emoji. Para esto, usamos el constructor con closures:

Label {
    VStack(alignment: .leading) {
        Text("Juan Pérez")
            .font(.headline)
        Text("Desarrollador iOS")
            .font(.caption)
            .foregroundColor(.secondary)
    }
} icon: {
    Image("foto_perfil")
        .resizable()
        .scaledToFill()
        .frame(width: 40, height: 40)
        .clipShape(Circle())
        .overlay(Circle().stroke(Color.blue, lineWidth: 2))
}

Este enfoque convierte a Label en un contenedor superpoderoso, manteniendo la semántica de “Título + Icono” pero permitiendo una libertad visual total.


3. Estilizando Labels: El modificador .labelStyle

Aquí es donde Label brilla sobre el HStack. SwiftUI nos permite separar el contenido de la presentación.

Imagina que tienes la misma etiqueta en una lista (donde quieres ver icono y texto) y en una barra de herramientas (donde quizás solo quieres el icono por falta de espacio). Con HStack tendrías que usar if/else. Con Label, usas estilos.

Estilos Integrados (Built-in Styles)

SwiftUI viene con varios estilos predefinidos que se adaptan a las normas de Apple:

  1. DefaultLabelStyle (o .automatic): El comportamiento por defecto. Muestra icono y título, a menos que el contenedor (como una Toolbar) dicte lo contrario.
  2. IconOnlyLabelStyle: Oculta el texto y muestra solo el icono. Ideal para barras de botones compactas.
Label("Buscar", systemImage: "magnifyingglass")
    .labelStyle(.iconOnly)
  1. Nota de Accesibilidad: Aunque el texto está oculto visualmente, VoiceOver lo sigue leyendo. ¡Es perfecto!
  2. TitleOnlyLabelStyle: Oculta el icono y muestra solo el texto.

Aplicando estilos en cascada

Puedes aplicar un estilo a un contenedor padre, y todos los Label dentro heredarán ese estilo.

VStack {
    Label("Uno", systemImage: "1.circle")
    Label("Dos", systemImage: "2.circle")
    Label("Tres", systemImage: "3.circle")
}
.labelStyle(.iconOnly) // Todos los labels se convierten en iconos

4. Personalización Avanzada: Creando tus propios LabelStyle

Los estilos por defecto son útiles, pero limitados. ¿Qué pasa si quieres que el icono esté encima del texto? ¿O si quieres que el icono tenga un fondo de color?

Para esto, creamos un estilo personalizado conformando al protocolo LabelStyle.

La Anatomía de LabelStyle

El protocolo requiere una función makeBody(configuration:). La configuration nos da acceso a dos propiedades mágicas:

  • configuration.title: La vista del título.
  • configuration.icon: La vista del icono.

Tutorial: Creando un estilo “Botón Vertical”

Vamos a crear un estilo común en paneles de control (Dashboards): un icono grande con el texto debajo.

struct VerticalLabelStyle: LabelStyle {
    func makeBody(configuration: Configuration) -> some View {
        VStack(spacing: 8) {
            configuration.icon
                .font(.system(size: 32)) // Escalamos el icono
                .foregroundColor(.blue)
            
            configuration.title
                .font(.caption)
                .fontWeight(.bold)
                .foregroundColor(.primary)
        }
        .padding()
        .background(Color.blue.opacity(0.1))
        .cornerRadius(10)
    }
}

Cómo usar tu estilo personalizado

Ahora puedes aplicar este estilo limpiamente en tu vista:

HStack {
    Label("Wi-Fi", systemImage: "wifi")
    Label("Bluetooth", systemImage: "wave.3.right")
    Label("Datos", systemImage: "antenna.radiowaves.left.and.right")
}
.labelStyle(VerticalLabelStyle()) // Aplicamos nuestra struct

Para hacerlo aún más “SwiftUI-nativo”, podemos extender LabelStyle:

extension LabelStyle where Self == VerticalLabelStyle {
    static var verticalCard: VerticalLabelStyle { VerticalLabelStyle() }
}

// Uso final:
Label("Wi-Fi", systemImage: "wifi")
    .labelStyle(.verticalCard)

5. Cambiando Colores e Imágenes

La personalización de colores en Label ha mejorado mucho con las versiones recientes de iOS.

Colores Simples

Si aplicas .foregroundStyle(.red) a un Label, tanto el texto como el icono se teñirán de rojo (a menos que uses un estilo personalizado que anule esto).

Label("Error", systemImage: "exclamationmark.triangle")
    .foregroundStyle(.red)

Colores Multicolor en SF Symbols

SF Symbols soporta renderizado multicolor. Label respeta esto automáticamente.

Label("Tiempo", systemImage: "cloud.sun.rain.fill")
    .symbolRenderingMode(.multicolor)

Esto mostrará la nube blanca, el sol amarillo y la lluvia azul, todo dentro de tu etiqueta.

Coloreando partes individualmente

Si quieres que el icono sea verde y el texto negro, tienes dos opciones:

Opción A: Inicializador con Closure

Label {
    Text("Éxito").foregroundStyle(.black)
} icon: {
    Image(systemName: "checkmark.circle.fill").foregroundStyle(.green)
}

Opción B: LabelStyle Personalizado (Más reutilizable)

struct ColoredIconStyle: LabelStyle {
    var iconColor: Color
    
    func makeBody(configuration: Configuration) -> some View {
        HStack {
            configuration.icon.foregroundStyle(iconColor)
            configuration.title.foregroundStyle(.primary)
        }
    }
}

// Uso:
Label("Verificado", systemImage: "checkmark.seal")
    .labelStyle(ColoredIconStyle(iconColor: .blue))

6. Diferencias entre Plataformas: iOS vs macOS vs watchOS

SwiftUI busca la escritura única (“Write once”), pero el comportamiento se adapta.

iOS y iPadOS

  • En List (estilo InsetGrouped), el icono del Label no se muestra por defecto con el color de acento del sistema, pero se alinea perfectamente a la izquierda.
  • En Menu (menús contextuales), iOS automáticamente mueve el icono al lado derecho del texto, ignorando el orden de tu código para cumplir con las guías de diseño de Apple. Usar Label es la única forma de obtener este comportamiento nativo.

macOS

  • Las barras laterales (Sidebar) en macOS dependen mucho de Label.
  • En macOS, los iconos de los Labels en las barras de herramientas a menudo se comportan como botones sin bordes.
  • El tamaño de fuente por defecto es diferente.

watchOS

  • En el Apple Watch, el espacio horizontal es oro. Los Label en listas a menudo se usan para aprovechar el espacio vertical si el texto es largo.
  • El uso de .labelStyle(.iconOnly) es muy común en complicaciones o botones de acción rápida.

7. Casos de Uso Prácticos y Trucos

El Truco de la Lista de Configuración

Un diseño clásico de iOS es la lista de Ajustes: un icono con un fondo de color en un cuadrado redondeado y el texto al lado. Vamos a crear un estilo para esto.

struct SettingsLabelStyle: LabelStyle {
    var backgroundColor: Color
    
    func makeBody(configuration: Configuration) -> some View {
        HStack {
            configuration.icon
                .foregroundColor(.white)
                .frame(width: 28, height: 28)
                .background(backgroundColor)
                .cornerRadius(6)
            
            configuration.title
                .font(.body)
        }
    }
}

// Ejemplo de uso
List {
    Label("Modo Avión", systemImage: "airplane")
        .labelStyle(SettingsLabelStyle(backgroundColor: .orange))
    
    Label("Wi-Fi", systemImage: "wifi")
        .labelStyle(SettingsLabelStyle(backgroundColor: .blue))
}

Animaciones (iOS 17+)

Con la llegada de iOS 17, Label soporta los nuevos efectos de símbolos (Symbol Effects).

@State private var isLoading = false

Label("Sincronizando", systemImage: "arrow.triangle.2.circlepath")
    .symbolEffect(.pulse, isActive: isLoading)
    .onTapGesture {
        isLoading.toggle()
    }

Esto hará que el icono pulse suavemente sin que tengas que escribir lógica de animación compleja.


8. Accesibilidad: El poder oculto de Label

Como mencionamos al principio, la accesibilidad es una razón clave para usar Label.

Si usas un HStack con una imagen y texto, VoiceOver podría leer: “Imagen, flecha derecha, Siguiente”. Esto es redundante.

Al usar Label("Siguiente", systemImage: "arrow.right"):

  1. Si el estilo es .titleOnly, VoiceOver lee “Siguiente”.
  2. Si el estilo es .iconOnly, VoiceOver lee “Siguiente” (usando el texto del título como la etiqueta de accesibilidad del icono).
  3. Si es estándar, el sistema agrupa los elementos para que se perciban como una única acción interactiva.

Consejo Pro: Si usas una imagen personalizada decorativa dentro de un Label, el sistema suele ser inteligente, pero siempre es bueno verificar.


Conclusión

El componente Label en SwiftUI es un ejemplo perfecto de la filosofía del framework: simple por fuera, potente por dentro.

Pasar de usar HStack manuales a Label no solo limpiará tu código, haciéndolo más legible y fácil de mantener, sino que automáticamente mejorará la experiencia de usuario en términos de accesibilidad y consistencia de diseño en todo el ecosistema de Apple.

Resumen de pasos para dominar Label:

  1. Úsalo siempre que tengas un par Título + Icono.
  2. Usa los inicializadores de closure para diseños complejos.
  3. Crea tus propios LabelStyle para reutilizar diseños en toda tu app.
  4. Aprovecha los modificadores de sistema como .symbolVariant y .symbolRenderingMode para dar vida a tus iconos.

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

ZStack en SwiftUI explicado con ejemplos

Next Article

Mejores aplicaciones en SwiftUI

Related Posts