Programación en Swift y SwiftUI para iOS Developers

Como crear una Splash Screen en SwiftUI

En el competitivo mundo de las aplicaciones móviles, la primera impresión es fundamental. Los primeros segundos de interacción de un usuario con tu app pueden definir su percepción completa de la misma. Aquí es donde entra en juego la Splash Screen (o pantalla de bienvenida).

Este tutorial es una guía exhaustiva que te llevará desde el concepto básico de una splash screen hasta la implementación de versiones avanzadas y profesionales en SwiftUI. Cubriremos no solo el “cómo”, sino también el “por qué”, asegurándonos de que entiendas las mejores prácticas y las directrices de diseño de Apple.

¿Qué es una Splash Screen?

Una Splash Screen es la pantalla inicial que aparece en una aplicación inmediatamente después de abrirla. En iOS, este concepto se divide en dos tipos:

1. Launch Screen (pantalla de lanzamiento del sistema)

Apple requiere que todas las apps tengan una pantalla inicial llamada Launch Screen, cuya función principal es dar la sensación de que la app arranca rápidamente, mostrando una imagen estática mientras se carga el entorno.

Características importantes:

  • Es estática (no puede tener animaciones).
  • Se configura desde Xcode.
  • Su función es únicamente técnica, no interactiva.
  • Debe imitar la apariencia inicial de la app para hacer que la transición sea fluida.
  • Apple no permite incluir texto dinámico o elementos cambiantes.

2. Splash Screen personalizada dentro de la app

En muchas apps se usa una pantalla animada o personalizada después de la Launch Screen.
Con SwiftUI puedes crear:

  • Logo con animaciones de aparición.
  • Transiciones hacia la app.
  • Pantallas temporizadas.
  • Efectos visuales como opacidad, escala y movimiento.

Esta pantalla  es animada y completamente personalizada, y forma parte de la app.

La Diferencia Crucial: Launch Screen vs. Splash Screen

Este es el punto más importante y que más confunde a los nuevos desarrolladores de iOS. No son lo mismo.

  • El “Launch Screen” (Pantalla de Lanzamiento):
    • Es una pantalla estática requerida por iOS.
    • No puede ejecutar código. No puedes hacer animaciones, llamadas a API, ni nada dinámico.
    • Antes, se definía con un LaunchScreen.storyboard. Ahora, la forma moderna y recomendada es configurarlo directamente en el Info.plist de tu proyecto.
    • Su propósito, según Apple, no es el branding, sino la percepción de rendimiento. Debe ser una “silueta” o versión ultraligera de la primera pantalla de tu app, para que la transición sea suave.
    • Se muestra antes de que tu app esté completamente cargada en memoria.
  • El “Splash Screen” (Pantalla de Bienvenida Dinámica):
    • Esta es la pantalla que construimos nosotros en SwiftUI.
    • Aparece inmediatamente después de que el Launch Screen estático desaparece.
    • Puede ejecutar código. Aquí es donde mostramos animaciones, cargamos datos, verificamos el login, etc.
    • Es la que usaremos para nuestro branding animado y la carga de datos.

En este tutorial, aprenderemos a crear la Splash Screen dinámica y, lo que es más importante, a hacer que la transición desde el Launch Screen estático sea perfectamente fluida.

Preparando el Proyecto en Xcode

Empecemos desde cero.

  1. Abre Xcode y selecciona “Create a new Xcode project”.
  2. Elige la plantilla “App” bajo la pestaña de iOS.
  3. Nombra tu proyecto, por ejemplo, SplashTutorial.
  4. Asegúrate de que la Interface esté en “SwiftUI” y el Life Cycle en “SwiftUI App”.
  5. Guarda el proyecto.

Para mantener nuestro código limpio, crearemos algunas vistas nuevas:

  1. SplashScreenView.swift: Esta será nuestra pantalla de bienvenida dinámica y animada.
  2. HomeView.swift: Esta será la pantalla principal de nuestra aplicación a la que llegaremos después de la splash screen.

Haz clic derecho en la carpeta de tu proyecto en el navegador de archivos de Xcode, selecciona “New File…”, elige “SwiftUI View” y crea estos dos archivos.


Método 1: La Splash Screen Básica (Basada en Tiempo)

Este es el método más simple. Mostraremos la splash screen durante un tiempo fijo (por ejemplo, 2.5 segundos) y luego pasaremos a la pantalla principal.

Advertencia: Este método no es ideal para aplicaciones reales. Si la app tarda más en cargar, el usuario verá una pantalla de carga después de la splash. Si carga más rápido, el usuario está esperando innecesariamente. Sin embargo, es un gran punto de partida para entender la mecánica.

1. Diseñando la SplashScreenView

Abre SplashScreenView.swift y añade el siguiente código. Crearemos un logo que se escala y se desvanece.

import SwiftUI

struct SplashScreenView: View {
    // 1. Estados para la animación
    @State private var scale = 0.7
    @State private var opacity = 0.5
    
    var body: some View {
        ZStack {
            // Puedes usar un color o un degradado
            Color.blue.ignoresSafeArea()
            
            VStack {
                Image(systemName: "swift") // Usa tu logo aquí
                    .font(.system(size: 100))
                    .foregroundColor(.white)
                Text("Mi App Increíble")
                    .font(.title)
                    .fontWeight(.bold)
                    .foregroundColor(.white.opacity(0.8))
            }
            // 2. Aplicar los efectos de estado
            .scaleEffect(scale)
            .opacity(opacity)
            // 3. El disparador de la animación
            .onAppear {
                withAnimation(.easeIn(duration: 1.5)) {
                    self.scale = 1.0
                    self.opacity = 1.0
                }
            }
        }
    }
}

struct SplashScreenView_Previews: PreviewProvider {
    static var previews: some View {
        SplashScreenView()
    }
}

Desglose del código:

  1. Usamos @State para propiedades que cambiarán y afectarán a la UI: scale y opacity.
  2. En el ZStack, superponemos nuestro logo y texto sobre un fondo azul. Aplicamos los modificadores .scaleEffect() y .opacity().
  3. El modificador .onAppear se dispara en cuanto la vista aparece. Dentro, usamos withAnimation para decirle a SwiftUI que anime los cambios a nuestros estados (scale y opacity) durante 1.5 segundos.

2. Diseñando la HomeView

Abre HomeView.swift. Esta es solo una pantalla de marcador de posición.

import SwiftUI

struct HomeView: View {
    var body: some View {
        NavigationView {
            ZStack {
                Color.gray.opacity(0.1).ignoresSafeArea()
                VStack {
                    Image(systemName: "house.fill")
                        .font(.system(size: 80))
                        .padding()
                    Text("¡Bienvenido a Casa!")
                        .font(.largeTitle)
                }
            }
            .navigationTitle("Home")
        }
    }
}

struct HomeView_Previews: PreviewProvider {
    static var previews: some View {
        HomeView()
    }
}

3. Controlando el Flujo

Ahora necesitamos una vista “raíz” que decida qué pantalla mostrar. Abriremos SplashTutorialApp.swift (el archivo @main) y modificaremos la estructura WindowGroup.

import SwiftUI

@main
struct SplashTutorialApp: App {
    @State private var showSplash = true // Controla la visibilidad

    var body: some Scene {
        WindowGroup {
            ZStack {
                // Si showSplash es true, muestra SplashScreenView
                if showSplash {
                    SplashScreenView()
                        .transition(.opacity) // Transición de fundido
                        .onAppear {
                            // Espera 2.5 segundos y luego cambia
                            DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) {
                                withAnimation {
                                    showSplash = false
                                }
                            }
                        }
                } else {
                    // Si es false, muestra HomeView
                    HomeView()
                        .transition(.opacity) // Transición de fundido
                }
            }
        }
    }
}

Desglose del código:

  • Usamos un @State private var showSplash = true.
  • Un ZStack contiene nuestra lógica. Usar un ZStack con un if/else permite aplicar transiciones (.transition(.opacity)) entre las vistas, haciendo que el cambio sea suave.
  • En la SplashScreenView, usamos .onAppear para disparar un temporizador.
  • DispatchQueue.main.asyncAfter es la forma de “esperar X segundos” en Swift.
  • Después de 2.5 segundos, cambiamos showSplash a false. Esto debe estar dentro de un bloque withAnimation para que el cambio de vistas (if/else) también se anime.

¡Ejecuta la app! Verás tu animación de 1.5s, y la pantalla permanecerá visible por un total de 2.5s antes de desvanecerse para revelar la HomeView.


Método 2: El Splash Screen Avanzado (Basado en Carga)

Este es el método profesional y recomendado. La splash screen se mostrará exactamente el tiempo que tarden en completarse tus tareas de carga (comprobar login, llamar a una API, etc.).

Usaremos el patrón ObservableObject (ViewModel) para gestionar el estado de carga de nuestra aplicación.

1. Creando un ViewModel de Carga

Crea un nuevo archivo Swift (no SwiftUI View) llamado AppViewModel.swift.

import Foundation
import Combine // O puedes usar async/await moderno

class AppViewModel: ObservableObject {
    
    // Publicamos esta variable. Cuando cambie, la UI se actualizará.
    @Published var isLoading: Bool = true
    
    // 1. Añadimos un estado para la autenticación
    @Published var isAuthenticated: Bool = false
    
    init() {
        // En cuanto se cree el ViewModel, empieza a cargar
        loadInitialData()
    }
    
    func loadInitialData() {
        // Usamos Tareas (async/await) para simular trabajo
        Task {
            // Simula una llamada a API que tarda 2 segundos
            // Reemplaza esto con tu llamada real:
            // let userData = try? await APIService.fetchUser()
            try? await Task.sleep(nanoseconds: 2_000_000_000) // 2 segundos
            
            // Simula una comprobación de login que tarda 1 segundo más
            // let authStatus = await AuthService.checkLogin()
            try? await Task.sleep(nanoseconds: 1_000_000_000) // 1 segundo
            
            // Al terminar, actualiza los estados en el hilo principal
            await MainActor.run {
                // isAuthenticated = authStatus // Lógica real
                isAuthenticated = false // Simulación: el usuario no está logueado
                
                withAnimation {
                    isLoading = false
                }
            }
        }
    }
}

Desglose del código:

  1. Usamos @Published para que cualquier vista que observe este objeto reaccione a los cambios de isLoading y isAuthenticated.
  2. Usamos Task para crear una tarea asíncrona moderna (async/await).
  3. Task.sleep simula el trabajo en red. En una app real, aquí es donde llamarías a tus APIs.
  4. await MainActor.run es crucial. Garantiza que las actualizaciones de la UI (@Published var) ocurran en el hilo principal.
  5. Envolvemos isLoading = false en withAnimation para una transición suave.

2. Creando una Vista de Login

Necesitamos una pantalla a la que ir si el usuario no está autenticado. Crea LoginView.swift.

import SwiftUI

struct LoginView: View {
    var body: some View {
        ZStack {
            Color.orange.ignoresSafeArea()
            VStack {
                Text("Login")
                    .font(.largeTitle)
                    .foregroundColor(.white)
                // ... aquí irían los campos de texto y el botón
            }
        }
    }
}

3. Actualizando la Lógica Raíz

Ahora, volvemos a SplashTutorialApp.swift para usar nuestro nuevo ViewModel.

import SwiftUI

@main
struct SplashTutorialApp: App {
    
    // 1. Creamos una instancia de nuestro ViewModel
    // Usamos @StateObject para que SwiftUI gestione su ciclo de vida
    @StateObject private var appViewModel = AppViewModel()

    var body: some Scene {
        WindowGroup {
            ZStack {
                // 2. La lógica ahora depende del ViewModel
                if appViewModel.isLoading {
                    SplashScreenView() // Nuestra misma splash de antes
                        .transition(.opacity)
                } else if appViewModel.isAuthenticated {
                    // 3. Si terminó de cargar Y está autenticado
                    HomeView()
                        .transition(.opacity)
                } else {
                    // 4. Si terminó de cargar Y NO está autenticado
                    LoginView()
                        .transition(.opacity)
                }
            }
            // 5. Inyectamos el ViewModel en el entorno
            // (Opcional pero buena práctica si HomeView/LoginView lo necesitan)
            .environmentObject(appViewModel) 
        }
    }
}

Desglose del código:

  1. Usamos @StateObject para crear y mantener viva nuestra instancia de AppViewModel mientras la app se ejecute.
  2. La lógica if/else ahora es mucho más potente.
  3. Si isLoading es true, mostramos la splash.
  4. Cuando isLoading se vuelve false, el ZStack reevalúa su contenido.
  5. Si isAuthenticated es true, muestra HomeView.
  6. Si isAuthenticated es false, muestra LoginView.

¡Ejecuta la app! Ahora la SplashScreenView se mostrará exactamente durante 3 segundos (los 2+1 que simulamos) y luego, basándose en el estado isAuthenticated, navegará a HomeView o LoginView.

El Toque Final: Sincronizando el Launch Screen Estático

Tenemos un último problema. Si ejecutas la app, verás esto:

  1. Una pantalla blanca (el Launch Screen por defecto).
  2. Luego aparece nuestra SplashScreenView azul.

Este “destello” (blanco a azul) es poco profesional. La solución es hacer que el Launch Screen estático y el primer fotograma de nuestra SplashScreenView sean idénticos.

Configurando el Info.plist

  1. En Xcode, ve al navegador de proyectos y haz clic en el nombre de tu proyecto (el icono azul).
  2. Selecciona tu “Target” y ve a la pestaña “Info”.
  3. En “Custom iOS Target Properties”, busca “Launch Screen” (o UILaunchScreen). Si no existe, añade una nueva fila con ese nombre.
  4. Haz clic en el + junto a “Launch Screen” para añadir claves dentro de este diccionario.
  5. Añade una clave llamada Background color (tipo String) y ponle el nombre del color que definiste en tus Assets (por ejemplo, si tienes un color “SplashBackground” en tu Asset Catalog, escribe SplashBackground). Si usas un color del sistema, como el azul de nuestro ejemplo, es más complicado.
  6. El método más fácil: Añade una clave Image Name (tipo String) y pon el nombre de una imagen de tu Asset Catalog. Esta imagen se centrará en la pantalla.
  7. Añade una clave Background color y pon el nombre de un color de tu Asset Catalog.

El Flujo de Trabajo Profesional:

  1. Ve a tu Assets.xcassets.
  2. Crea un “Color Set”. Nómbralo SplashBackgroundColor. Ajústalo al mismo color azul que usamos en SplashScreenView.
  3. Crea un “Image Set”. Nómbralo SplashLogo. Añade la imagen de tu logo aquí (la versión estática).
  4. Actualiza Info.plist:
    • Background color -> SplashBackgroundColor
    • Image Name -> SplashLogo
    • Añade la clave Show image as template (tipo Boolean) y ponla en YES o NO dependiendo de si quieres que se tinte o no.
  5. Actualiza SplashScreenView.swift:
ZStack {
    // Usa el color desde los Assets
    Color("SplashBackgroundColor").ignoresSafeArea() 

    VStack {
        // Usa la imagen desde los Assets
        Image("SplashLogo") 
            .resizable()
            .scaledToFit()
            .frame(width: 100, height: 100)
        // ... resto del texto
    }
    .scaleEffect(scale)
    // ... etc
}

Ahora, cuando la app se inicie:

  1. iOS muestra inmediatamente el SplashBackgroundColor con el SplashLogo estático (desde Info.plist).
  2. Tu app de SwiftUI carga en segundo plano.
  3. Tu SplashScreenView aparece. Como su fondo (SplashBackgroundColor) y su logo (SplashLogo) son idénticos al Launch Screen, el usuario no percibe ningún cambio.
  4. Inmediatamente, tu animación .onAppear se dispara, y el logo estático “cobra vida” con la animación de escala.

Esta transición fluida es la marca de una aplicación pulida y de alta calidad.


Conclusión y Buenas Prácticas

Has aprendido a diferenciar entre el Launch Screen estático y el Splash Screen dinámico, a implementar una versión simple basada en tiempo y una versión avanzada y robusta basada en carga de datos y autenticación usando un ViewModel.

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

Recuerda estas reglas de oro:

  • No Hagas Esperar al Usuario: El propósito de la splash no es ser una animación larga, sino ocultar un tiempo de carga inevitable. Haz que tu carga de datos sea lo más rápida posible.
  • Sincroniza el Estilo: La clave de la fluidez es que el Launch Screen (Info.plist) y el primer fotograma de tu SplashScreenView (SwiftUI) sean idénticos.
  • Informa si es Necesario: Si tu carga realmente va a tardar más de 5-6 segundos (lo cual deberías evitar), considera añadir un indicador de progreso (ProgressView) a tu SplashScreenView para que el usuario sepa que la app no está bloqueada.
  • Usa Async/Await: Adopta las nuevas características de concurrencia de Swift (Taskasync/awaitMainActor) para un código de carga más limpio y seguro.
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Article

Como usar WebView y WebPage en SwiftUI

Related Posts