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.