¿Qué hace Diseñador HIG de Apple?
Diseña aplicaciones iOS hermosas y nativas siguiendo las Guías de Interfaz Humana (HIG) de Apple. Crea interfaces accesibles e intuitivas con componentes nativos, tipografía adecuada, colores semánticos y los principios de diseño de Apple.
Qué Hace Esta Habilidad
Te ayuda a diseñar y construir aplicaciones iOS que se sientan nativas y sigan las pautas de Apple:
- Generar Componentes iOS - Crea componentes SwiftUI y UIKit
- Validar Diseños - Verifica el cumplimiento con las HIG de Apple
- Garantizar Accesibilidad - VoiceOver, Tipo Dinámico, contraste de color
- Aplicar Principios de Diseño - Claridad, Deferencia, Profundidad
- Usar Colores Semánticos - Soporte automático de modo oscuro
- Implementar Tipografía - Sistema de fuente San Francisco
- Seguir Espaciado - Sistema de cuadrícula de 8pt y áreas seguras
Principios de Diseño de Apple
1. Claridad
Haz que el contenido sea claro y enfocado.
El texto es legible en todos los tamaños, los iconos son precisos y lúcidos, los adornos son sutiles y apropiados, y un enfoque en la funcionalidad impulsa el diseño.
// ✅ Contenido claro y enfocado
Text("Bienvenida de nuevo, Sarah")
.font(.title)
.foregroundColor(.primary)
// ❌ Poco claro y desordenado
Text("Bienvenida de nuevo, Sarah!!!")
.font(.title)
.foregroundColor(.red)
.background(.yellow)
.overlay(Image(systemName: "star.fill"))
2. Deferencia
La interfaz ayuda a las personas a entender e interactuar con el contenido, pero nunca compite con él.
La interfaz se aplana ante el contenido, utilizando un tratamiento visual ligero que mantiene el enfoque en el contenido y le da espacio para respirar.
// ✅ Enfocado en el contenido
VStack(alignment: .leading, spacing: 8) {
Text("Título del Artículo")
.font(.headline)
Text("El contenido del artículo va aquí...")
.font(.body)
.foregroundColor(.secondary)
}
.padding()
// ❌ Interfaz distractiva
VStack(spacing: 8) {
Text("Título del Artículo")
.font(.headline)
.foregroundColor(.white)
.background(.blue)
.border(.red, width: 3)
}
3. Profundidad
Las capas visuales y el movimiento realista transmiten jerarquía y ayudan a las personas a entender las relaciones.
Capas visuales distintas y movimiento realista imparten vitalidad y facilitan la comprensión. El tacto y la descubribilidad aumentan el deleite y permiten el acceso a la funcionalidad sin perder contexto.
// ✅ Jerarquía de profundidad clara
ZStack {
Color(.systemBackground)
VStack {
// Tarjeta con elevación
CardView()
.shadow(radius: 8)
}
}
// Usando desenfoque para la profundidad
Text("Contenido")
.background(.ultraThinMaterial)
Componentes de la Interfaz de iOS
Patrones de Navegación
1. Barra de Navegación
Barra superior para navegación y acciones.
NavigationStack {
List {
Text("Elemento 1")
Text("Elemento 2")
}
.navigationTitle("Título")
.navigationBarTitleDisplayMode(.large)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Añadir") {
// Acción
}
}
}
}
Pautas:
- Usa títulos grandes para vistas de nivel superior
- Usa títulos en línea para vistas de detalle
- Mantén las acciones relevantes al contexto actual
- Máximo 2-3 elementos de barra de herramientas
2. Barra de Pestañas
Navegación inferior para destinos de nivel superior.
TabView {
HomeView()
.tabItem {
Label("Inicio", systemImage: "house")
}
SearchView()
.tabItem {
Label("Buscar", systemImage: "magnifyingglass")
}
ProfileView()
.tabItem {
Label("Perfil", systemImage: "person")
}
}
Pautas:
- Máximo 3-5 pestañas
- Usa Símbolos SF para los iconos
- Las etiquetas deben ser concisas (una palabra)
- Nunca ocultes ni deshabilites pestañas
- No uses la barra de pestañas con la barra de herramientas en la misma vista
3. Lista
Lista desplazable de elementos.
List {
Section("Hoy") {
ForEach(items) { item in
NavigationLink {
DetailView(item: item)
} label: {
HStack {
Image(systemName: item.icon)
.foregroundColor(.accentColor)
Text(item.title)
}
}
}
}
}
.listStyle(.insetGrouped)
Estilos de Lista:
.plain- Filas de borde a borde.insetGrouped- Secciones redondeadas e insertadas (predeterminado iOS).sidebar- Para barras laterales de navegación
4. Hoja (Modal)
Presenta contenido de forma modal.
struct ContentView: View {
@State private var showSheet = false
var body: some View {
Button("Mostrar Detalles") {
showSheet = true
}
.sheet(isPresented: $showSheet) {
DetailView()
.presentationDetents([.medium, .large])
}
}
}
Detenciones de Hoja:
.medium- Media pantalla.large- Pantalla completa- Alturas personalizadas disponibles
Controles de Formulario
1. Botón
Control de acción principal.
// Botón relleno (acción principal)
Button("Continuar") {
// Acción
}
.buttonStyle(.borderedProminent)
// Botón con borde (acción secundaria)
Button("Cancelar") {
// Acción
}
.buttonStyle(.bordered)
// Botón simple (acción terciaria)
Button("Saber Más") {
// Acción
}
.buttonStyle(.plain)
Jerarquía de Botones:
- Prominente - Acción principal (uno por pantalla)
- Con Borde - Acciones secundarias
- Simple - Acciones terciarias, enlaces
Pautas:
- Objetivo táctil mínimo: 44x44 puntos
- Usa verbos para las etiquetas de los botones
- Haz que las acciones destructivas requieran confirmación
2. Campo de Texto
Control de entrada de texto.
@State private var username = ""
@State private var password = ""
VStack(alignment: .leading, spacing: 16) {
// Campo de texto estándar
TextField("Nombre de usuario", text: $username)
.textFieldStyle(.roundedBorder)
.textContentType(.username)
.textInputAutocapitalization(.never)
.autocorrectionDisabled()
// Campo seguro
SecureField("Contraseña", text: $password)
.textFieldStyle(.roundedBorder)
.textContentType(.password)
}
Tipos de Contenido de Texto:
.username- Campo de nombre de usuario.password- Campo de contraseña.emailAddress- Campo de correo electrónico.telephoneNumber- Número de teléfono.creditCardNumber- Tarjeta de crédito
3. Alternador
Control booleano (interruptor).
@State private var isEnabled = false
Toggle("Activar notificaciones", isOn: $isEnabled)
.toggleStyle(.switch)
Pautas:
- La etiqueta describe lo que controla el alternador
- El efecto debe ser inmediato
- Úsalo solo para opciones binarias
4. Selector
Control de selección.
@State private var selectedSize = "Mediano"
let sizes = ["Pequeño", "Mediano", "Grande"]
// Estilo de menú
Picker("Tamaño", selection: $selectedSize) {
ForEach(sizes, id: \.self) { size in
Text(size).tag(size)
}
}
.pickerStyle(.menu)
// Estilo segmentado (para 2-5 opciones)
Picker("Tamaño", selection: $selectedSize) {
ForEach(sizes, id: \.self) { size in
Text(size).tag(size)
}
}
.pickerStyle(.segmented)
Estilos de Selector:
.menu- Menú desplegable (predeterminado).segmented- Control segmentado (2-5 opciones).wheel- Rueda desplazable.inline- Lista en línea (en formularios)
Tarjetas y Contenedores
Vista de Tarjeta
struct CardView: View {
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("Título")
.font(.headline)
Text("Descripción aquí con algunos detalles sobre el contenido.")
.font(.subheadline)
.foregroundColor(.secondary)
.lineLimit(2)
Spacer()
Button("Acción") {
// Acción
}
.buttonStyle(.borderedProminent)
}
.padding()
.frame(width: 300, height: 200)
.background(Color(.systemBackground))
.cornerRadius(12)
.shadow(color: .black.opacity(0.1), radius: 8, x: 0, y: 4)
}
}
Tipografía
Sistema de Fuente San Francisco
La fuente del sistema de Apple diseñada para una legibilidad óptima.
// Estilos de texto de Tipo Dinámico
Text("Título Grande").font(.largeTitle) // 34pt
Text("Título").font(.title) // 28pt
Text("Título 2").font(.title2) // 22pt
Text("Título 3").font(.title3) // 20pt
Text("Título").font(.headline) // 17pt seminegrita
Text("Cuerpo").font(.body) // 17pt regular
Text("Llamada").font(.callout) // 16pt
Text("Subtítulo").font(.subheadline) // 15pt
Text("Nota al pie").font(.footnote) // 13pt
Text("Leyenda").font(.caption) // 12pt
Text("Leyenda 2").font(.caption2) // 11pt
Fuentes Personalizadas con Tipo Dinámico
// Fuente personalizada que escala con Tipo Dinámico
Text("Texto Personalizado")
.font(.custom("TuFuente-Regular", size: 17, relativeTo: .body))
Pesos de Fuente
Text("Ligero").fontWeight(.light)
Text("Regular").fontWeight(.regular)
Text("Medio").fontWeight(.medium)
Text("Seminegrita").fontWeight(.semibold)
Text("Negrita").fontWeight(.bold)
Text("Pesado").fontWeight(.heavy)
Pautas de Tipografía
Hacer:
- ✅ Usar la fuente del sistema (San Francisco) para consistencia
- ✅ Soporte de Tipo Dinámico para accesibilidad
- ✅ Usar estilos de texto semánticos (.headline, .body, etc.)
- ✅ Texto de cuerpo mínimo: 17pt
- ✅ Espaciado entre líneas: 120-145% del tamaño de fuente
No hacer:
- ❌ Usar demasiados tamaños de fuente (ceñirse a los estilos del sistema)
- ❌ Hacer texto más pequeño de 11pt
- ❌ Usar mayúsculas para texto largo
- ❌ Deshabilitar Tipo Dinámico
Colores
Colores Semánticos
Colores que se adaptan automáticamente al modo claro/oscuro.
// Colores de Elementos de Interfaz
Color(.label) // Texto principal
Color(.secondaryLabel) // Texto secundario
Color(.tertiaryLabel) // Texto terciario
Color(.quaternaryLabel) // Texto de marca de agua
Color(.systemBackground) // Fondo principal
Color(.secondarySystemBackground) // Fondo secundario
Color(.tertiarySystemBackground) // Fondo terciario
Color(.systemFill) // Colores de relleno
Color(.secondarySystemFill)
Color(.tertiarySystemFill)
Color(.quaternarySystemFill)
Color(.separator) // Líneas separadoras
Color(.opaqueSeparator) // Separador no transparente
Colores del Sistema
// Colores del sistema estándar (se adaptan al modo oscuro)
Color(.systemRed)
Color(.systemOrange)
Color(.systemYellow)
Color(.systemGreen)
Color(.systemMint)
Color(.systemTeal)
Color(.systemCyan)
Color(.systemBlue)
Color(.systemIndigo)
Color(.systemPurple)
Color(.systemPink)
Color(.systemBrown)
Color(.systemGray)
Colores Personalizados con Modo Oscuro
// Definir color adaptativo
Extension Color {
static let customBackground = Color("CustomBackground")
}
// En Assets.xcassets, crea un conjunto de colores con:
// - Cualquier Apariencia: #FFFFFF
// - Apariencia Oscura: #000000
Pautas de Contraste de Color
Cumplimiento WCAG AA:
- Texto normal: relación de contraste mínima 4.5:1
- Texto grande (24pt+): relación de contraste mínima 3:1
- Componentes de interfaz: relación de contraste 3:1
Colores personalizados:
- Prueba con Contraste Aumentado activado
- Apunta a 7:1 para texto crítico
- Proporciona contraste suficiente en ambos modos
Espaciado y Diseño
Sistema de Cuadrícula de 8 Puntos
Todo el espaciado debe ser múltiplo de 8.
// Valores de espaciado
.padding(8) // 8pt
.padding(16) // 16pt (estándar)
.padding(24) // 24pt
.padding(32) // 32pt
.padding(40) // 40pt
.padding(48) // 48pt
// Espaciado específico de bordes
.padding(.horizontal, 16)
.padding(.vertical, 24)
.padding(.top, 16)
.padding(.bottom, 16)
Áreas Seguras
Respeta las áreas seguras del dispositivo.
// Contenido dentro del área segura (predeterminado)
VStack {
Text("Contenido")
}
// Extender más allá del área segura
VStack {
Color.blue
}
.ignoresSafeArea()
// Extender solo la parte superior
VStack {
Color.blue
}
.ignoresSafeArea(edges: .top)
Objetivos Táctiles
Tamaño interactivo mínimo: 44x44 puntos.
Button("Tocar") {
// Acción
}
.frame(minWidth: 44, minHeight: 44)
Pautas de Espaciado
// Espaciado de componentes
VStack(spacing: 8) { // Espaciado ajustado
Text("Línea 1")
Text("Línea 2")
}
VStack(spacing: 16) { // Espaciado estándar
Text("Sección 1")
Text("Sección 2")
}
VStack(spacing: 24) { // Espaciado suelto
SectionView()
SectionView()
}
Accesibilidad
Soporte de VoiceOver
Lector de pantalla para usuarios ciegos o con baja visión.
// Etiqueta accesible
Image(systemName: "heart.fill")
.accessibilityLabel("Favorito")
// Valor accesible
Slider(value: $volume)
.accessibilityLabel("Volumen")
.accessibilityValue("\(Int(volume * 100))%")
// Pista accesible
Button("Compartir") {
share()
}
.accessibilityHint("Comparte este elemento con otros")
// Agrupar elementos
HStack {
Image(systemName: "person")
Text("Juan Pérez")
}
.accessibilityElement(children: .combine)
// Oculto de VoiceOver
Image("decorative")
.accessibilityHidden(true)
Tipo Dinámico
Soporta el tamaño de texto preferido del usuario.
// Soportado automáticamente con fuentes del sistema
Text("Este texto escala")
.font(.body)
// Limitar la escala (si es necesario)
Text("Este texto tiene límites")
.font(.body)
.dynamicTypeSize(...DynamicTypeSize.xxxLarge)
// Fuente personalizada con Tipo Dinámico
Text("Fuente personalizada")
.font(.custom("TuFuente", size: 17, relativeTo: .body))
Daltonismo
Diseña para usuarios daltónicos.
// No te bases solo en el color
HStack {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.green)
Text("Éxito")
}
// No solo color
Circle()
.fill(.green)
// ❌ Solo color
// Mejor con forma/icono
HStack {
Image(systemName: "checkmark.circle.fill")
Circle().fill(.green)
}
// ✅ Color + forma
Reducir Movimiento
Respeta las preferencias de movimiento del usuario.
@Environment(\.accessibilityReduceMotion) var reduceMotion
var animation: Animation {
reduceMotion ? .none : .spring()
}
Button("Animar") {
withAnimation(animation) {
// Animar
}
}
Aumentar Contraste
Soporta el modo de alto contraste.
...
[truncado para traducción]