Programación en Swift y SwiftUI para iOS Developers

Dynamic Type en SwiftUI

Para cualquier iOS Developer moderno, la accesibilidad ya no es una característica opcional; es una responsabilidad fundamental. Una de las piedras angulares de una aplicación accesible es el Dynamic Type. Esta tecnología permite a los usuarios elegir el tamaño del texto que prefieren visualizar en todo el sistema operativo, y es crucial que nuestras aplicaciones respeten esa elección sin romper la interfaz de usuario.

En este tutorial profundo de programación Swift, aprenderás qué es el Dynamic Type, por qué es vital, y cómo implementarlo magistralmente usando Dynamic Type en SwiftUI. Veremos cómo esto se aplica no solo a iOS, sino que extenderemos el conocimiento a macOS y watchOS utilizando Xcode y Swift.


¿Qué es Dynamic Type?

Dynamic Type es una característica de Apple que permite al usuario ajustar el tamaño del texto que se muestra en la pantalla a través de los ajustes de Accesibilidad del dispositivo. No se trata solo de hacer el texto más grande para personas con discapacidades visuales; también se trata de comodidad. Muchos usuarios prefieren un texto ligeramente más grande para reducir la fatiga visual, mientras que otros prefieren un texto más pequeño para ver más contenido a la vez.

Como iOS Developer, adoptar Dynamic Type te ofrece los siguientes beneficios:

  1. Mejora la Accesibilidad: Tu app se vuelve utilizable para una audiencia mucho más amplia, incluyendo personas con presbicia u otras condiciones visuales.
  2. Cumplimiento de Directrices: Apple recomienda encarecidamente (y a veces exige en sus propias apps) el soporte de Dynamic Type. Una app que ignora esto se siente “rota” o poco profesional.
  3. Interfaz Adaptable: Al usar Dynamic Type, obligas a tu diseño a ser flexible. Esto hace que tu app se adapte mejor a diferentes tamaños de pantalla, no solo a diferentes tamaños de fuente.
  4. Experiencia de Usuario Superior: Respetar las preferencias del usuario a nivel de sistema crea una experiencia más integrada y satisfactoria.

Fundamentos de Dynamic Type en SwiftUI

SwiftUI ha simplificado drásticamente la implementación de Dynamic Type en comparación con UIKit. En la programación Swift tradicional con UIKit, a menudo teníamos que registrar observadores para las notificaciones de cambio de tamaño de fuente y actualizar manualmente las etiquetas. En SwiftUI, la mayor parte del trabajo pesado se hace automáticamente.

Estilos de Fuente de Texto Estándar

La forma más sencilla de soportar Dynamic Type es usar los estilos de fuente predefinidos de SwiftUI. Estos estilos están ligados automáticamente a los tamaños de Dynamic Type del sistema.

import SwiftUI

struct FontStylesView: View {
    var body: some View {
        ScrollView {
            VStack(alignment: .leading, spacing: 10) {
                Text("Large Title").font(.largeTitle)
                Text("Title 1").font(.title)
                Text("Title 2").font(.title2)
                Text("Title 3").font(.title3)
                Text("Headline").font(.headline)
                Text("Subheadline").font(.subheadline)
                Text("Body").font(.body) // Este es el predeterminado
                Text("Callout").font(.callout)
                Text("Footnote").font(.footnote)
                Text("Caption 1").font(.caption)
                Text("Caption 2").font(.caption2)
            }
            .padding()
        }
    }
}

Al usar modificadores como .font(.body) o .font(.title), le estás diciendo a SwiftUI: “Usa la fuente del sistema para el cuerpo (o título), y ajusta su tamaño de acuerdo con la preferencia del usuario”. Si el usuario aumenta el tamaño del texto en los ajustes del iPhone, el texto .body crecerá automáticamente.

Prueba en Xcode: Puedes probar esto fácilmente en el Canvas de Xcode. Haz clic en el botón de “Attributes Inspector” (el panel derecho) mientras seleccionas el Preview, y busca la opción de “Dynamic Type”. Mueve el deslizador para ver cómo tu interfaz se adapta en tiempo real.


Creando Fuentes Personalizadas con Soporte para Dynamic Type

A menudo, las directrices de diseño de tu marca requieren una fuente personalizada que no es la del sistema (San Francisco). Como iOS Developer, debes saber cómo aplicar Dynamic Type a estas fuentes personalizadas. Si simplemente usas un tamaño fijo, ignorarás la preferencia del usuario.

A partir de iOS 14, SwiftUI introdujo un método elegante en Font para escalar fuentes personalizadas.

Uso de .custom(_:size:relativeTo:)

Este es el método clave. Debes especificar el nombre de tu fuente, el tamaño base (el que usarías si el Dynamic Type estuviera en el ajuste predeterminado) y, lo más importante, a qué estilo de texto del sistema se debe relacionar.

import SwiftUI

struct CustomFontDynamicTypeView: View {
    var body: some View {
        VStack(spacing: 20) {
            // Usando una fuente personalizada (asegúrate de que esté cargada en tu proyecto)
            Text("Encabezado de Marca")
                .font(.custom("OpenSans-Bold", size: 28, relativeTo: .title))
            
            Text("Este es un párrafo de ejemplo que usa una fuente personalizada pero respeta el Dynamic Type. Si el usuario aumenta el tamaño de fuente del sistema, este texto también crecerá, usando el estilo '.body' como referencia para su escalado.")
                .font(.custom("OpenSans-Regular", size: 16, relativeTo: .body))
            
            Text("Pie de página")
                .font(.custom("OpenSans-LightItalic", size: 12, relativeTo: .footnote))
        }
        .padding()
    }
}

En este ejemplo de programación Swift:

  • "Encabezado de Marca" tiene un tamaño base de 28. Se escalará relativamente a cómo el sistema escala .title. Si .title crece un 20%, tu fuente personalizada también crecerá un 20% desde 28.
  • Es crucial elegir el relativeTo correcto. Un texto de cuerpo debe relacionarse con .body, un título con .title, etc., para que el comportamiento de escalado sea consistente con el resto del sistema.

Nota: No olvides añadir los archivos de fuente (.ttf o .otf) a tu proyecto de Xcode, asegurarte de que estén incluidos en el Target correspondiente y añadirlos a la clave “Fonts provided by application” en tu Info.plist.


Adaptando el Diseño de la Interfaz (Layout) para Textos Grandes

Aquí es donde muchos desarrolladores fallan. Implementar la fuente escalable es solo el primer paso. El verdadero desafío para un iOS Developer es asegurar que el diseño de la app no se rompa cuando el texto crece significativamente. Un texto grande puede desbordar contenedores, superponerse a otros elementos o empujar controles fuera de la pantalla.

SwiftUI proporciona herramientas potentes para manejar esto.

Usando la Variable de Entorno @Environment

La variable de entorno sizeCategory te permite leer el tamaño de Dynamic Type actual del usuario. Puedes usar esto para cambiar condicionalmente tu diseño.

import SwiftUI

struct AdaptableLayoutView: View {
    // Leemos la categoría de tamaño actual
    @Environment(\.sizeCategory) var sizeCategory
    
    var body: some View {
        ScrollView {
            // Decidimos el diseño basándonos en el tamaño
            if sizeCategory.isAccessibilityCategory {
                // Diseño para tamaños de accesibilidad (muy grandes)
                accessibilityLayout
            } else {
                // Diseño estándar
                standardLayout
            }
        }
    }
    
    // Un diseño horizontal para tamaños normales
    var standardLayout: some View {
        HStack {
            Image(systemName: "person.circle.fill")
                .font(.system(size: 60))
            VStack(alignment: .leading) {
                Text("Juan Pérez")
                    .font(.title)
                Text("Desarrollador iOS Senior")
                    .font(.subheadline)
            }
        }
        .padding()
    }
    
    // Un diseño vertical para tamaños de accesibilidad
    var accessibilityLayout: some View {
        VStack(spacing: 15) {
            Image(systemName: "person.circle.fill")
                .font(.system(size: 80))
            Text("Juan Pérez")
                .font(.largeTitle) // Título más grande para accesibilidad
            Text("Desarrollador iOS Senior")
                .font(.title2) // Subtítulo más grande
                .multilineTextAlignment(.center)
        }
        .padding()
    }
}

sizeCategory.isAccessibilityCategory es una propiedad de conveniencia que devuelve true si el tamaño está dentro de los rangos de Accesibilidad (XS, S, M, L, XL son normales; AX1 a AX5 son de accesibilidad). En este ejemplo, cambiamos de un HStack a un VStack cuando el texto es muy grande, evitando que la imagen y el texto se compriman horizontalmente.

Uso Inteligente de ViewThatFits (iOS 16+)

Introducido en Xcode 14 (iOS 16), ViewThatFits es una joya para el diseño adaptable. Evalúa una lista de vistas y elige la primera que encaja en el espacio disponible sin truncar el texto.

import SwiftUI

struct ViewThatFitsExample: View {
    var body: some View {
        VStack {
            Text("Estado de la Conexión:")
                .font(.headline)
            
            ViewThatFits {
                // Intento 1: Diseño horizontal con descripción completa
                HStack {
                    Label("Conectado Seguramente", systemImage: "lock.shield.fill")
                        .font(.body)
                    Spacer()
                    Text("v1.5")
                        .font(.caption)
                }
                
                // Intento 2: Diseño horizontal con texto más corto
                HStack {
                    Label("Conectado", systemImage: "lock.shield.fill")
                        .font(.body)
                    Spacer()
                    Text("v1.5")
                        .font(.caption)
                }
                
                // Intento 3: Diseño vertical (fallback para texto muy grande)
                VStack {
                    Label("Conectado Seguramente", systemImage: "lock.shield.fill")
                        .font(.body)
                    Text("Versión 1.5")
                        .font(.caption)
                }
            }
            .padding()
            .background(Color.secondary.opacity(0.1))
            .cornerRadius(10)
            
        }
        .padding()
    }
}

Con ViewThatFits, si el Dynamic Type está en un nivel bajo, se elegirá el primer HStack. A medida que el usuario aumenta el tamaño del texto, la descripción larga puede que ya no quepa horizontalmente, por lo que ViewThatFits saltará automáticamente al segundo o tercer diseño. Esto reduce enormemente la necesidad de lógica condicional manual basada en @Environment.


Dynamic Type Multiplataforma: macOS y watchOS

Como iOS Developer, tu conocimiento de SwiftUI te permite llevar tus aplicaciones a otras plataformas de Apple. Afortunadamente, los principios de Dynamic Type que hemos visto se aplican casi de la misma manera en macOS y watchOS, con algunas consideraciones específicas.

Dynamic Type en watchOS

En watchOS, el Dynamic Type es quizás más crítico que en iOS, dado el tamaño extremadamente reducido de la pantalla. Los usuarios a menudo necesitan aumentar el tamaño del texto para leer rápidamente de un vistazo mientras están en movimiento.

Directrices para watchOS:

  1. Usa Estilos del Sistema: Prefiere .body, .footnote, etc. watchOS escalará estos estilos agresivamente.
  2. Usa .custom(_:size:relativeTo:): Si usas fuentes personalizadas, relaciónalas siempre con un estilo del sistema.
  3. Layouts Verticales: El diseño por defecto en watchOS es vertical (VStack dentro de una List o ScrollView). Esto facilita que el texto crezca hacia abajo sin romper el diseño horizontal.

Dynamic Type en macOS

En macOS, el Dynamic Type no funciona exactamente igual que en iOS. Los usuarios no tienen un deslizador de “Tamaño de Texto” global para todas las aplicaciones. Sin embargo, las aplicaciones individuales a menudo permiten al usuario cambiar el tamaño de la fuente (piensa en Mail, Notas o Safari).

Además, macOS soporta los “Tamaños de Pantalla” (Scaled resolutions), que afectan cómo se renderiza todo el sistema.

Implementación en macOS con SwiftUI:

Usar .font(.body) o .font(.custom(..., relativeTo: .body)) sigue siendo la mejor práctica. Aunque el usuario no tenga un control global de Dynamic Type, usar los estilos del sistema asegura que tu aplicación se comporte de manera consistente con otras aplicaciones nativas de macOS y respete las configuraciones de escala de la pantalla.

Si quieres ofrecer un control de tamaño de fuente dentro de tu app de macOS, puedes usar una variable @AppStorage para guardar la preferencia del usuario y aplicarla como un tamaño base en tu fuente personalizada:

import SwiftUI

// Este código funciona en macOS
struct MacOSFontSizeControlView: View {
    // Guardamos el tamaño de fuente base preferido por el usuario
    @AppStorage("preferredBaseFontSize") var baseFontSize: Double = 16.0
    
    var body: some View {
        VStack(spacing: 20) {
            Text("Texto ajustable en macOS")
                // Escalamos la fuente personalizada basándonos en la preferencia
                .font(.custom("HelveticaNeue", size: CGFloat(baseFontSize), relativeTo: .body))
            
            HStack {
                Button("A") { baseFontSize -= 2 }
                    .disabled(baseFontSize <= 10)
                Slider(value: $baseFontSize, in: 10...30, step: 2)
                    .frame(width: 150)
                Button("A") { baseFontSize += 2 }
                    .font(.title3)
                    .disabled(baseFontSize >= 30)
            }
            .padding()
        }
        .padding()
    }
}

Aunque este no es el Dynamic Type “automático” de iOS, es la forma recomendada en la programación Swift moderna para macOS para dar control al usuario.


Escalado de Imágenes y Símbolos (SF Symbols)

El Dynamic Type no solo afecta al texto. Como iOS Developer, debes saber que los elementos visuales que acompañan al texto también deben escalarse para mantener la armonía visual.

SF Symbols y Dynamic Type

La forma más fácil de lograr esto es usar SF Symbols. SwiftUI trata los SF Symbols como texto.

HStack {
    // El símbolo SF se escalará automáticamente con el texto .title
    Image(systemName: "star.fill")
        .font(.title) 
    
    Text("Favorito")
        .font(.title)
}

Si le aplicas el modificador .font() a una Image que contiene un SF Symbol, el símbolo adoptará el tamaño y el grosor (weight) correspondientes a ese estilo de texto, escalándose perfectamente con Dynamic Type.

Escalado de Imágenes Personalizadas

Si tienes iconos personalizados que no son SF Symbols, debes escalarlos manualmente usando la variable de entorno sizeCategory o usar el modificador .dynamicTypeSize(...) (disponible en iOS 15+) para limitar su crecimiento si es necesario.

Una mejor opción es usar el modificador .resizable() y .aspectRatio(contentMode: .fit) dentro de un contenedor que use sizeCategory para cambiar su tamaño base.

@Environment(\.sizeCategory) var sizeCategory

var iconSize: CGFloat {
    // Calculamos el tamaño del icono basándonos en el Dynamic Type
    switch sizeCategory {
    case .accessibilityExtraLarge, .accessibilityExtraExtraLarge, .accessibilityExtraExtraExtraLarge:
        return 60
    case .accessibilityLarge:
        return 50
    default:
        return 40
    }
}

Image("myCustomIcon")
    .resizable()
    .aspectRatio(contentMode: .fit)
    .frame(width: iconSize, height: iconSize)

Mejores Prácticas y Consejos para el iOS Developer

  1. Nunca Deshabilites Dynamic Type: No uses tamaños de fuente fijos (.font(.system(size: 16))) a menos que tengas una razón de diseño extremadamente poderosa (y rara). Usar un tamaño fijo es ignorar activamente la accesibilidad.
  2. Usa multilineTextAlignment: Cuando el texto crece, es muy probable que ocupe varias líneas. Asegúrate de configurar un alineamiento correcto (ej. .center o .leading) para mantener la legibilidad.
  3. No Limites lineLimit Innecesariamente: Evita usar .lineLimit(1) en textos largos. Deja que el texto ocupe el espacio que necesita. Si debes limitarlo, asegúrate de que el diseño responda bien al truncado (.truncationMode).
  4. Prueba Agresivamente en Xcode: Usa el Canvas y el Simulador para probar tu app en los tamaños más pequeños y en los tamaños de accesibilidad más grandes (AX5). Te sorprenderá lo que se rompe.
  5. Combina con SF Symbols: Simplifica tu vida y usa SF Symbols siempre que sea posible; el escalado gratuito que obtienes vale la pena.
  6. Respeta las Áreas Seguras (Safe Areas): Cuando el texto empuja otros elementos, asegúrate de que no se superpongan a barras de navegación o tab bars. SwiftUI maneja esto bien, pero los diseños personalizados requieren atención.

Conclusión

Dominar el Dynamic Type en SwiftUI es un paso crucial para convertirte en un iOS Developer de élite. No se trata solo de escribir código, sino de empatía hacia tus usuarios. SwiftUI ha hecho que la implementación de interfaces accesibles y adaptables sea más fácil que nunca en la historia de la programación Swift.

Al usar los estilos del sistema, aplicar correctamente Dynamic Type a tus fuentes personalizadas con Xcode, y adaptar tus layouts de manera inteligente (especialmente con herramientas como ViewThatFits), estarás creando aplicaciones que no solo se ven bien en las capturas de pantalla del App Store, sino que ofrecen una experiencia de usuario excepcional y respetuosa para todos, en iOS, macOS y watchOS.

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

DisclosureGroup en SwiftUI

Next Article

@discardableResult en Swift

Related Posts