Pantallas

Mockups de App

Tres pantallas principales: Home, Detalle de Curso y Tab Bar — siguiendo HIG de Apple.

Home — Dashboard de Aprendizaje
9:41
●●● 📶 🔋
🔥 Racha actual: 7 días
Prompt Engineering Pro
Módulo 4 · Cadenas de razonamiento
62% completado 8 módulos restantes
📚
12
Cursos
⚡
47
Lecciones
🏆
3
Badges
Destacados
🤖
Agentes IA
Automatiza tu Agencia
6h 20m Nuevo
✍️
Copywriting
Copy con IA para Ads
3h 45m 35%
📊
Analytics
Datos con Claude Code
4h 10m Nuevo
Continuar
🧠
Prompt Engineering Pro
Módulo 4 · 18 min restantes
›
🎨
Design con IA · Figma+AI
Módulo 2 · 32 min restantes
›
🏠
Inicio
🔍
Explorar
📖
Mis Cursos
👤
Perfil
Detalle de Curso — NavigationStack
9:41
●●● 📶 🔋
← Explorar
Prompt Engineering Intermedio
Prompt Engineering Pro con Claude
Domina las técnicas avanzadas de prompting para agencias de marketing
6h 30m
Duración
12
Módulos
4.9 ⭐
Rating
1.2k
Alumnos
▶ Continuar curso
Módulos del curso
✓
1. Fundamentos del prompting
Completado · 38 min
✓
2. Estructura de instrucciones
Completado · 45 min
✓
3. Few-shot y ejemplos
Completado · 32 min
▶
4. Cadenas de razonamiento
En progreso · 18 min rest.
🔒
5. Roles y personas de IA
Bloqueado · 52 min
🔒
6. Prompts para marketing
Bloqueado · 41 min
🏠
Inicio
🔍
Explorar
📖
Mis Cursos
👤
Perfil

Identidad visual

Design Tokens iOS

Colores semánticos que se adaptan automáticamente a Light/Dark Mode siguiendo las directrices HIG.

systemGreen
#34C759
Acento principal · CTA
systemBlue
#007AFF
Links · Botones secundarios
systemOrange
#FF9500
Badges · Nivel intermedio
systemRed
#FF3B30
Errores · Alertas
systemGray6
#F2F2F7
Background grupado
systemGray
#8E8E93
Labels terciarios

SwiftUI Components

Componentes Generados

Código listo para Xcode. Cada componente sigue HIG, Dynamic Type y soporte Dark Mode automático.

CourseCard.swift
SwiftUI
// CultivaLearn · Tarjeta de curso para scroll horizontal import SwiftUI struct CourseCard: View { let course: Course var body: some View { VStack(alignment: .leading, spacing: 0) { // Hero Icon Area ZStack { RoundedRectangle(cornerRadius: 0) .fill(course.accentColor.opacity(0.15)) .frame(height: 90) Text(course.emoji) .font(.system(size: 40)) } VStack(alignment: .leading, spacing: 4) { // Category tag Text(course.category.uppercased()) .font(.caption2.weight(.semibold)) .foregroundStyle(course.accentColor) .kerning(0.5) Text(course.title) .font(.subheadline.weight(.semibold)) .foregroundStyle(.primary) .lineLimit(2) HStack { Text(course.duration) .font(.caption2) .foregroundStyle(.tertiary) Spacer() // Progress badge if let progress = course.progress { Text(progress) .font(.caption2.weight(.semibold)) .padding(.horizontal, 7) .padding(.vertical, 2) .background(course.accentColor.opacity(0.12), in: Capsule()) .foregroundStyle(course.accentColor) } } } .padding([.horizontal, .bottom], 12) .padding(.top, 10) } .frame(width: 180) .background(.background, in: RoundedRectangle(cornerRadius: 16)) .shadow(color: .black.opacity(0.07), radius: 8, y: 3) // Accessibility .accessibilityElement(children: .combine) .accessibilityLabel("\(course.title), \(course.category)") } }
ProgressDayCard.swift
SwiftUI
// CultivaLearn · Tarjeta de progreso diario con gradient import SwiftUI struct ProgressDayCard: View { let courseName: String let module: String let streak: Int let progress: Double // 0.0 – 1.0 var body: some View { VStack(alignment: .leading, spacing: 6) { Label("Racha: \(streak) días", systemImage: "flame.fill") .font(.subheadline.weight(.medium)) .foregroundStyle(.white.opacity(0.85)) Text(courseName) .font(.title3.weight(.bold)) .foregroundStyle(.white) Text(module) .font(.subheadline) .foregroundStyle(.white.opacity(0.8)) // Progress bar GeometryReader { geo in ZStack(alignment: .leading) { Capsule() .fill(.white.opacity(0.3)) .frame(height: 6) Capsule() .fill(.white) .frame( width: geo.size.width * progress, height: 6 ) .animation(.spring(duration: 0.6), value: progress) } } .frame(height: 6) HStack { Text("\(Int(progress * 100))% completado") Spacer() Text("\(Int((1 - progress) * 12)) módulos restantes") } .font(.caption) .foregroundStyle(.white.opacity(0.8)) } .padding(16) .background { LinearGradient( colors: [Color(.systemGreen), Color(hex: "#30D158")], startPoint: .topLeading, endPoint: .bottomTrailing ) } .clipShape(RoundedRectangle(cornerRadius: 16)) .shadow(color: Color(.systemGreen).opacity(0.4), radius: 12, y: 6) } }
ModuleRow.swift
SwiftUI
// CultivaLearn · Fila de módulo con estado (done/current/locked) import SwiftUI enum ModuleStatus { case done, current, locked } struct ModuleRow: View { let index: Int let title: String let duration: String let status: ModuleStatus var statusIcon: String { switch status { case .done: return "checkmark.circle.fill" case .current: return "play.circle.fill" case .locked: return "lock.circle.fill" } } var statusColor: Color { switch status { case .done: return .green case .current: return .blue case .locked: return .secondary } } var body: some View { HStack(spacing: 12) { Image(systemName: statusIcon) .font(.title3) .foregroundStyle(statusColor) .frame(width: 28) VStack(alignment: .leading, spacing: 2) { Text("\(index). \(title)") .font(.subheadline.weight(.semibold)) .foregroundStyle( status == .locked ? .tertiary : .primary ) Text(status == .current ? "En progreso · \(duration)" : duration) .font(.caption) .foregroundStyle(status == .current ? Color.blue : .tertiary) } Spacer() if status != .locked { Image(systemName: "chevron.right") .font(.caption.weight(.semibold)) .foregroundStyle(.tertiary) } } .padding(12) .background { if status == .current { RoundedRectangle(cornerRadius: 10) .fill(Color.blue.opacity(0.05)) .overlay( RoundedRectangle(cornerRadius: 10) .strokeBorder(Color.blue.opacity(0.3), lineWidth: 1.5) ) } else { RoundedRectangle(cornerRadius: 10) .fill(Color(.systemBackground)) } } .accessibilityLabel("\(title), \(status == .locked ? "bloqueado" : duration)") } }
CultivaLearnApp.swift
SwiftUI
// CultivaLearn · TabView principal + NavigationStack (iOS 18+) import SwiftUI @main struct CultivaLearnApp: App { var body: some Scene { WindowGroup { MainTabView() } } } struct MainTabView: View { @State private var selectedTab = 0 @State private var path = NavigationPath() var body: some View { // TabView fluido iOS 18+ TabView(selection: $selectedTab) { Tab("Inicio", systemImage: "house", value: 0) { NavigationStack(path: $path) { HomeView() .navigationDestination( for: Course.self) { course in CourseDetailView(course: course) } } } Tab("Explorar", systemImage: "magnifyingglass", value: 1) { NavigationStack { ExploreView() } } Tab("Mis Cursos", systemImage: "book", value: 2) { NavigationStack { MyCourses() } } Tab("Perfil", systemImage: "person", value: 3) { NavigationStack { ProfileView() } } } // Tint global → .systemGreen de Apple .tint(.green) } }

Validación HIG

Checklist Human Interface Guidelines

Verificación de cumplimiento de las directrices de diseño de Apple para la app CultivaLearn.

✅
Colores semánticos
Uso de .primary, .secondary, .background para soporte automático de Dark Mode sin código adicional.
✅
SF Symbols únicamente
house, magnifyingglass, book, person — consistencia con el lenguaje visual del sistema iOS.
✅
Dynamic Type
Fuentes semánticas (.headline, .subheadline, .body) — escalan con las preferencias de accesibilidad del usuario.
✅
VoiceOver compatible
.accessibilityLabel() y .accessibilityElement(children: .combine) en todos los componentes interactivos.
✅
NavigationStack iOS 16+
Uso de NavigationPath y .navigationDestination para navegación programática type-safe.
✅
TabView iOS 18 API
Nueva API Tab() con value: para binding programático y animaciones fluidas del sistema.
✅
Materiales y blur
.ultraThinMaterial en nav bar y tab bar — efecto glassmorphism nativo que respeta el contenido subyacente.
✅
Grid adaptativo iPad
LazyVGrid con GridItem(.adaptive) — se adapta a cualquier tamaño de pantalla: iPhone SE, iPhone Pro Max, iPad.
✅
Lazy rendering
LazyVStack/LazyVGrid para listas largas — renderizado diferido que mantiene 60fps en scroll.