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 sí 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 elInfo.plistde 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.
- Abre Xcode y selecciona “Create a new Xcode project”.
- Elige la plantilla “App” bajo la pestaña de iOS.
- Nombra tu proyecto, por ejemplo,
SplashTutorial. - Asegúrate de que la Interface esté en “SwiftUI” y el Life Cycle en “SwiftUI App”.
- Guarda el proyecto.
Para mantener nuestro código limpio, crearemos algunas vistas nuevas:
SplashScreenView.swift: Esta será nuestra pantalla de bienvenida dinámica y animada.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:
- Usamos
@Statepara propiedades que cambiarán y afectarán a la UI:scaleyopacity. - En el
ZStack, superponemos nuestro logo y texto sobre un fondo azul. Aplicamos los modificadores.scaleEffect()y.opacity(). - El modificador
.onAppearse dispara en cuanto la vista aparece. Dentro, usamoswithAnimationpara decirle a SwiftUI que anime los cambios a nuestros estados (scaleyopacity) 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
ZStackcontiene nuestra lógica. Usar unZStackcon unif/elsepermite aplicar transiciones (.transition(.opacity)) entre las vistas, haciendo que el cambio sea suave. - En la
SplashScreenView, usamos.onAppearpara disparar un temporizador. DispatchQueue.main.asyncAfteres la forma de “esperar X segundos” en Swift.- Después de 2.5 segundos, cambiamos
showSplashafalse. Esto debe estar dentro de un bloquewithAnimationpara 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:
- Usamos
@Publishedpara que cualquier vista que observe este objeto reaccione a los cambios deisLoadingyisAuthenticated. - Usamos
Taskpara crear una tarea asíncrona moderna (async/await). Task.sleepsimula el trabajo en red. En una app real, aquí es donde llamarías a tus APIs.await MainActor.runes crucial. Garantiza que las actualizaciones de la UI (@Published var) ocurran en el hilo principal.- Envolvemos
isLoading = falseenwithAnimationpara 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:
- Usamos
@StateObjectpara crear y mantener viva nuestra instancia deAppViewModelmientras la app se ejecute. - La lógica
if/elseahora es mucho más potente. - Si
isLoadingestrue, mostramos la splash. - Cuando
isLoadingse vuelvefalse, elZStackreevalúa su contenido. - Si
isAuthenticatedestrue, muestraHomeView. - Si
isAuthenticatedesfalse, muestraLoginView.
¡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:
- Una pantalla blanca (el Launch Screen por defecto).
- Luego aparece nuestra
SplashScreenViewazul.
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
- En Xcode, ve al navegador de proyectos y haz clic en el nombre de tu proyecto (el icono azul).
- Selecciona tu “Target” y ve a la pestaña “Info”.
- En “Custom iOS Target Properties”, busca “Launch Screen” (o
UILaunchScreen). Si no existe, añade una nueva fila con ese nombre. - Haz clic en el
+junto a “Launch Screen” para añadir claves dentro de este diccionario. - Añade una clave llamada
Background color(tipoString) y ponle el nombre del color que definiste en tus Assets (por ejemplo, si tienes un color “SplashBackground” en tu Asset Catalog, escribeSplashBackground). Si usas un color del sistema, como el azul de nuestro ejemplo, es más complicado. - El método más fácil: Añade una clave
Image Name(tipoString) y pon el nombre de una imagen de tu Asset Catalog. Esta imagen se centrará en la pantalla. - Añade una clave
Background colory pon el nombre de un color de tu Asset Catalog.
El Flujo de Trabajo Profesional:
- Ve a tu
Assets.xcassets. - Crea un “Color Set”. Nómbralo
SplashBackgroundColor. Ajústalo al mismo color azul que usamos enSplashScreenView. - Crea un “Image Set”. Nómbralo
SplashLogo. Añade la imagen de tu logo aquí (la versión estática). - Actualiza
Info.plist:Background color->SplashBackgroundColorImage Name->SplashLogo- Añade la clave
Show image as template(tipoBoolean) y ponla enYESoNOdependiendo de si quieres que se tinte o no.
- 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:
- iOS muestra inmediatamente el
SplashBackgroundColorcon elSplashLogoestático (desdeInfo.plist). - Tu app de SwiftUI carga en segundo plano.
- Tu
SplashScreenViewaparece. Como su fondo (SplashBackgroundColor) y su logo (SplashLogo) son idénticos al Launch Screen, el usuario no percibe ningún cambio. - Inmediatamente, tu animación
.onAppearse 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 tuSplashScreenView(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 tuSplashScreenViewpara que el usuario sepa que la app no está bloqueada. - Usa Async/Await: Adopta las nuevas características de concurrencia de Swift (
Task,async/await,MainActor) para un código de carga más limpio y seguro.








