List es una View contenedora en SwiftUI para mostrar datos como una lista verticalmente desplazable, análoga a UITableView en UIKit. Según Apple Developer Documentation, 2024, List admite secciones estáticas y dinámicas, acciones de deslizamiento, reordenamiento de filas y pull-to-refresh. A diferencia de UITableView, List utiliza una API declarativa basada en SwiftUI y ForEach, gestionando automáticamente la reutilización de celdas y el rendimiento con una gran cantidad de filas.
Puntos clave
List es una View que muestra una secuencia de elementos en una lista verticalmente desplazable. Se introdujo en iOS 13 junto con SwiftUI y es la forma principal de mostrar listas de datos, reemplazando a UITableView de UIKit. List gestiona automáticamente la reutilización de celdas, el desplazamiento y el rendimiento.
List utiliza lazy-loading: las celdas se crean a medida que se desplaza, no todas a la vez. Esto lo diferencia de VStack con ForEach dentro de ScrollView, donde todas las celdas se crean en el momento del renderizado. List también proporciona soporte integrado para acciones de deslizamiento, pull-to-refresh, edición (eliminar/mover) y selección de filas.
Según Apple WWDC 2021 (Session 10072), List en iOS 15+ recibió mejoras significativas de rendimiento gracias a un nuevo mecanismo de diffing a nivel de colecciones. Esto hizo que List fuera más eficiente al actualizar datos, especialmente para listas con cientos de filas.
Los desarrolladores a menudo eligen entre List y ScrollView con VStack para mostrar un conjunto de Views. La diferencia clave: List utiliza reutilización de celdas (como UITableView), mientras que ScrollView + VStack crea todas las Views inmediatamente. Para listas de tamaño fijo (hasta 20 elementos), la diferencia es insignificante. Para listas dinámicas con 50+ filas, List es preferible por rendimiento.
Lista estática es una lista con un número fijo de filas especificadas directamente en el cuerpo de List. Se utiliza para menús, configuraciones y formularios con un conjunto conocido de elementos. Cada fila se declara explícitamente, sin bucles ni ForEach.
// Lista estática (para menús y configuraciones)
List {
Text("Perfil")
Text("Configuraciones")
Text("Acerca de")
}
// Lista dinámica (para datos)
struct UserList: View {
let users: [User]
var body: some View {
List(users) { user in
HStack {
Text(user.name)
Text(user.role)
.foregroundColor(.secondary)
}
}
}
}
Lista dinámica utiliza el inicializador List(data:rowContent:) o ForEach dentro del cuerpo de List. El primer enfoque es conveniente cuando cada fila corresponde a un elemento de datos. El segundo es útil cuando hay secciones o elementos adicionales entre los datos.
Identificación (Identifiable): Para listas dinámicas, los elementos de datos deben cumplir con el protocolo Identifiable, o debe especificar un KeyPath a un identificador único en la tupla data:id. SwiftUI utiliza identificadores para rastrear cambios: adiciones, eliminaciones y movimientos de filas.
Section es una View para agrupar filas en una List con un encabezado y un footer opcional. Section acepta header y footer como ViewBuilder, lo que permite usar no solo texto sino también Views personalizadas para los encabezados de sección.
struct SettingsView: View {
var body: some View {
List {
Section(header: Text("Cuenta")) {
Text("Nombre")
Text("Correo electrónico")
}
Section(header: Text("Notificaciones")) {
Toggle("Push", isOn: $pushEnabled)
Toggle("Email", isOn: $emailEnabled)
}
}
.listStyle(.insetGrouped)
}
}
// Secciones dinámicas con ForEach
List {
ForEach(groupedData.keys.sorted(), id: \.self) { key in
Section(header: Text(key)) {
ForEach(groupedData[key]!) { item in
Text(item.title)
}
}
}
}
Estilos de List: SwiftUI proporciona varios estilos integrados a través del modificador .listStyle(). .insetGrouped — estándar para iOS Settings, .plain — minimalista, .inset — con sangría, .sidebar — para Sidebar en iPad.
Según SwiftUI Cookbook (2024), Section con secciones dinámicas y ForEach dentro es un patrón estándar para agrupar datos en aplicaciones con estructura compleja. La regla clave: no anide Section dentro de Section, y no use el inicializador List(data:) junto con Section — use ForEach dentro del cuerpo de List.
.swipeActions(edge:allowsFullSwipe:content:) — un modificador para iOS 15+ que agrega acciones de deslizamiento a las filas de List. Permite mostrar botones al deslizar hacia la izquierda (predeterminado) o hacia la derecha, con diferentes colores y roles (destructive, cancel).
struct TaskList: View {
@Binding var tasks: [Task]
var body: some View {
List {
ForEach($tasks) { $task in
Text(task.title)
.swipeActions(edge: .trailing) {
Button("Eliminar", role: .destructive) {
tasks.removeAll { $0.id == task.id }
}
}
.swipeActions(edge: .leading) {
Button(task.isDone ? "Deshacer" : "Hecho") {
task.isDone.toggle()
}
.tint(.green)
}
}
}
.refreshable {
// Carga de datos asíncrona
await loadTasks()
}
}
}
.refreshable — un modificador para iOS 15+ que agrega pull-to-refresh. Acepta un closure asíncrono que se ejecuta cuando el usuario tira de la lista hacia abajo. SwiftUI muestra automáticamente un indicador de carga. Una vez que la operación se completa, el indicador se oculta.
.onDelete y .onMove — modificadores para iOS 13+ que agregan soporte para eliminación y reordenamiento de filas. Para usarlos, envuelva los datos en ForEach con Binding o pase closures a través de .onDelete(perform:) en List o ForEach.
El rendimiento de List depende del número de filas, la complejidad de cada celda y la frecuencia de las actualizaciones de datos. SwiftUI utiliza lazy-loading y reutilización de celdas (similar a UITableView.dequeueReusableCell), pero pueden ser necesarias optimizaciones adicionales para listas con 500+ filas.
| Optimización | Descripción | Versión iOS |
|---|---|---|
| Identifiable | IDs únicos para cada elemento | iOS 13+ |
| EquatableView | Evita redibujar cuando los datos son iguales | iOS 13+ |
| id(_:) | Fuerza la recreación de View cuando cambia el ID | iOS 13+ |
| .equatable() | Comparación estricta por Equatable | iOS 15+ |
| Diffable data | Diff automático en cambios | iOS 15+ |
Problema 1: Actualizaciones frecuentes. Si los datos en la lista se actualizan con frecuencia (por ejemplo, cada segundo), List puede redibujar las celdas visibles en cada cambio de estado. Solución: use estructuras (value types) para los datos — SwiftUI las compara por valor y redibuja solo las filas cambiadas.
Problema 2: Celdas pesadas. Si cada fila contiene una jerarquía de Views compleja, imágenes y animaciones, el desplazamiento puede ralentizarse. Solución: extraiga las celdas en Views separadas, use EquatableView para evitar redibujados innecesarios. Según SwiftUI Lab (2024), dividir una fila compleja en subcomponentes reduce el tiempo de renderizado en un 30–50%.
Problema 3: Gran número de filas. Con 1000+ filas, List sigue funcionando eficientemente gracias al lazy-loading, pero la carga inicial puede ralentizarse debido al cálculo del layout. Solución: use LazyVStack solo para listas con filas uniformes donde no se necesiten funciones de List (deslizamiento, secciones). Para listas con todas las funciones, List sigue siendo la mejor opción.
Preguntas frecuentes
List es una View contenedora para mostrar una lista de datos desplazable en SwiftUI. Es el equivalente de UITableView en UIKit con una API declarativa. Admite secciones, acciones de deslizamiento, pull-to-refresh, edición y personalización a través de .listStyle().
List utiliza lazy-loading y reutilización de celdas — las celdas se crean a medida que se desplaza. ScrollView + VStack crea todas las Views a la vez. Para listas con 50+ filas, List es preferible. Para conjuntos fijos pequeños (hasta 20 elementos), la diferencia es insignificante.
Use el modificador .refreshable (iOS 15+). Pase un closure asíncrono con la lógica de actualización de datos. SwiftUI muestra automáticamente un indicador de carga y lo oculta después de que la operación asíncrona se completa.
Use Section View con un encabezado y footer opcional. Coloque las filas de la lista dentro de Section. Para secciones dinámicas, use ForEach con groupedData. El estilo de la lista se configura mediante .listStyle(.insetGrouped) para una apariencia similar a iOS.
Use estructuras (value types) para los datos, extraiga celdas complejas en Views separadas con EquatableView, evite actualizaciones de estado frecuentes en cada fila. Para listas con 1000+ filas, considere LazyVStack si no se necesitan funciones de List.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también