17 nov 2025·1 min de lectura

Ajustes de rendimiento en SwiftUI para listas largas: soluciones prácticas

Ajustes de rendimiento en SwiftUI para listas largas: soluciones prácticas para re-renders, identidad estable de filas, paginación, carga de imágenes y desplazamiento fluido en iPhones antiguos.

Ajustes de rendimiento en SwiftUI para listas largas: soluciones prácticas

Cómo se ven las “listas lentas” en apps SwiftUI reales

Una “lista lenta” en SwiftUI normalmente no es un bug. Es el momento en que tu UI no puede seguir el ritmo del dedo. Lo notas al desplazar: la lista titubea, se pierden frames y todo se siente pesado.

Señales típicas:

  • El desplazamiento se entrecorta, especialmente en dispositivos más antiguos
  • Las filas parpadean o muestran contenido erróneo brevemente
  • Los taps se sienten retrasados, o las acciones swipe empiezan tarde
  • El teléfono se calienta y la batería se drena más rápido de lo esperado
  • El uso de memoria crece cuanto más scroll haces

Las listas largas pueden sentirse lentas aunque cada fila parezca “pequeña”, porque el coste no es sólo dibujar píxeles. SwiftUI aún tiene que averiguar qué es cada fila, calcular el layout, resolver fuentes e imágenes, ejecutar tu código de formateo y diffear actualizaciones cuando los datos cambian. Si cualquiera de ese trabajo ocurre con demasiada frecuencia, la lista se convierte en un punto caliente.

También ayuda separar dos ideas. En SwiftUI, un “re-render” a menudo significa que se recompone el body de una vista. Esa parte suele ser barata. El trabajo caro es lo que desencadena la recomposición: layout pesado, decodificación de imágenes, medición de texto o reconstruir muchas filas porque SwiftUI piensa que su identidad cambió.

Imagina un chat con 2.000 mensajes. Mensajes nuevos llegan cada segundo y cada fila formatea timestamps, mide texto multilínea y carga avatares. Incluso si sólo añades un item, un cambio de estado mal acotado puede hacer que muchas filas se re-evalúen y algunas se redibujen.

El objetivo no es microoptimizar. Quieres desplazamiento fluido, taps instantáneos y actualizaciones que toquen sólo las filas que realmente cambiaron. Las soluciones abajo se centran en identidad estable, filas más baratas, menos actualizaciones innecesarias y carga controlada.

Las causas principales: identidad, trabajo por fila y tormentas de actualizaciones

Cuando una lista SwiftUI se siente lenta, rara vez es por “demasiadas filas”. Es trabajo extra ocurriendo mientras desplazas: reconstruir filas, recalcular layout o recargar imágenes una y otra vez.

La mayoría de las causas raíz caen en tres categorías:

  • Identidad inestable: las filas no tienen un id consistente, o usas \\.self` para valores que pueden cambiar. SwiftUI no puede emparejar filas antiguas con nuevas, así que reconstruye más de lo necesario.
  • Demasiado trabajo por fila: formateo de fechas, filtrado, redimensionado de imágenes o trabajo de red/disco dentro de la vista de fila.
  • Tormentas de actualizaciones: un cambio (escribir, un timer, actualizaciones de progreso) dispara frecuentes actualizaciones de estado y la lista se refresca repetidamente.

Ejemplo: tienes 2.000 órdenes. Cada fila formatea moneda, construye un NSAttributedString y empieza una petición de imagen. Mientras tanto, un timer “última sincronización” actualiza una vez por segundo en la vista padre. Aunque los datos de las órdenes no cambien, ese timer puede invalidar la lista lo suficiente como para hacer que el scroll se sienta entrecortado.

Por qué List y LazyVStack pueden sentirse distintos

List es más que un scroll view. Está diseñado alrededor del comportamiento de tabla/colección y optimizaciones de sistema. A menudo maneja grandes datasets con menos memoria, pero puede ser sensible a la identidad y a actualizaciones frecuentes.

ScrollView + LazyVStack te da más control sobre el layout y los visuales, pero también es más fácil hacer trabajo extra de layout por accidente o desencadenar actualizaciones caras. En dispositivos más antiguos, ese trabajo extra se nota antes.

Antes de reescribir tu UI, mide primero. Arreglos pequeños como IDs estables, sacar trabajo de las filas y reducir el churn de estado suelen resolver el problema sin cambiar el contenedor.

Arregla la identidad de las filas para que SwiftUI pueda diffear eficientemente

Cuando una lista larga se siente entrecortada, la identidad suele ser la culpable. SwiftUI decide qué filas pueden reutilizarse comparando IDs. Si esos IDs cambian, SwiftUI trata las filas como nuevas, descarta las antiguas y reconstruye más de lo necesario. Eso puede parecer re-renders aleatorios, pérdida de posición de scroll o animaciones que se disparan sin razón.

La ganancia más simple: haz que el id de cada fila sea estable y esté ligado a tu fuente de datos.

Un error común es generar la identidad dentro de la vista:

ForEach(items) { item in
  Row(item: item)
    .id(UUID())
}

Esto fuerza un nuevo ID en cada render, así que cada fila se vuelve “diferente” cada vez.

Prefiere IDs que ya existan en tu modelo, como una clave primaria de base de datos, un ID del servidor o un slug estable. Si no tienes uno, créalo una vez cuando creas el modelo — no dentro de la vista.

struct Item: Identifiable {
  let id: Int
  let title: String
}

List(items) { item in
  Row(item: item)
}

Ten cuidado con los índices. ForEach(items.indices, id: \\.\/self) (usa id: \\.self en casos reales) ata la identidad a la posición. Si insertas, eliminas o reordenas, las filas “se mueven” y SwiftUI puede reutilizar la vista equivocada para los datos equivocados. Usa índices sólo para arrays verdaderamente estáticos.

Si usas id: \\.self, asegúrate de que el valor Hashable del elemento sea estable en el tiempo. Si el hash cambia cuando un campo se actualiza, la identidad de la fila también cambia. Una regla segura para Equatable y Hashable: bájalos a un único ID estable, no a propiedades editables como name o isSelected.

Chequeos de cordura:

  • Los IDs vienen de la fuente de datos (no UUID() en la vista)
  • Los IDs no cambian cuando el contenido de la fila cambia
  • La identidad no depende de la posición en el array salvo que la lista nunca se reordene

Reduce re-renders haciendo las filas más baratas

Una lista larga a menudo se siente lenta porque cada fila hace demasiado trabajo cada vez que SwiftUI re-evalúa su body. La meta es simple: hacer que cada fila sea barata de reconstruir.

Un coste oculto común es pasar valores “grandes” a una fila. Structs grandes, modelos profundamente anidados o propiedades computadas pesadas pueden disparar trabajo extra aunque la UI parezca igual. Puede que estés reconstruyendo strings, parseando fechas, redimensionando imágenes o produciendo árboles de layout complejos más a menudo de lo que crees.

Saca el trabajo caro fuera del body

Si algo es lento, no lo vuelvas a construir dentro del body de la fila una y otra vez. Precomputalo cuando lleguen los datos, cachealo en tu view model o memoízalo en un helper pequeño.

Costes a nivel de fila que se suman rápido:

  • Crear un DateFormatter o NumberFormatter por fila
  • Formateo de strings pesado en body (joins, regex, parseo markdown)
  • Construir arrays derivados con .map o .filter dentro del body
  • Leer blobs grandes y convertirlos (como decodificar JSON) en la vista
  • Layout excesivamente complejo con muchos stacks anidados y condicionales

Un ejemplo simple: mantén formatters estáticos y pasa strings preformateadas a la fila.

enum Formatters {
    static let shortDate: DateFormatter = {
        let f = DateFormatter()
        f.dateStyle = .medium
        f.timeStyle = .none
        return f
    }()
}

struct OrderRow: View {
    let title: String
    let dateText: String

    var body: some View {
        HStack {
            Text(title)
            Spacer()
            Text(dateText).foregroundStyle(.secondary)
        }
    }
}

Divide filas y usa Equatable cuando convenga

Si sólo cambia una pequeña parte (como un contador), aíslala en una subvista para que el resto de la fila permanezca estable.

Para UI realmente basada en valores, hacer una subvista Equatable (o envolverla con EquatableView) puede ayudar a SwiftUI a saltarse trabajo cuando las entradas no cambiaron. Mantén las entradas equatables pequeñas y específicas — no todo el modelo.

Controla las actualizaciones de estado que disparan el refresco completo de la lista

Reduce las tormentas de actualizaciones en la fuente
Automatiza flujos con procesos de negocio visuales y mantén la lógica de tu app consistente en todas partes.
Iniciar proyecto

A veces las filas están bien, pero algo sigue indicando a SwiftUI que refresque toda la lista. Mientras desplazas, incluso pequeñas actualizaciones extra pueden convertirse en tartamudeos, especialmente en dispositivos antiguos.

Una causa común es recrear tu modelo con demasiada frecuencia. Si una vista padre se reconstruye y usaste @ObservedObject para un view model que la vista posee, SwiftUI puede recrearlo, resetear sus suscripciones y disparar publicaciones frescas. Si la vista es la propietaria del modelo, usa @StateObject para que se cree una sola vez y se mantenga estable. Usa @ObservedObject para objetos inyectados desde fuera.

Otro asesino silencioso de rendimiento es publicar con demasiada frecuencia. Timers, pipelines Combine y actualizaciones de progreso pueden dispararse muchas veces por segundo. Si una propiedad publicada afecta la lista (o está en un ObservableObject compartido por la pantalla), cada tick puede invalidar la lista.

Ejemplo: tienes un campo de búsqueda que actualiza query en cada pulsación y luego filtra 5.000 items. Si filtras inmediatamente, la lista se vuelve a diffear constantemente mientras el usuario escribe. Debouncea la query y actualiza el array filtrado después de una breve pausa.

Patrones que suelen ayudar:

  • Mantén valores de cambio rápido fuera del objeto que maneja la lista (usa objetos más pequeños o @State local)
  • Debouncea búsquedas y filtrados para que la lista se actualice tras una pausa al escribir
  • Evita publishes de timers de alta frecuencia; actualiza menos o sólo cuando el valor realmente cambie
  • Mantén el estado por-fila local (como @State en la fila) en lugar de un valor global que cambia constantemente
  • Divide modelos grandes: un ObservableObject para datos de la lista y otro para estado UI a nivel de pantalla

La idea es simple: haz que el tiempo de scroll sea silencioso. Si nada importante cambió, la lista no debería pedirse que haga trabajo.

Elige el contenedor correcto: List vs LazyVStack

El contenedor que elijas afecta cuánto trabajo hace iOS por ti.

List suele ser la opción más segura cuando tu UI se parece a una tabla estándar: filas con texto, imágenes, acciones swipe, selección, separadores, modo edición y accesibilidad. Bajo el capó se beneficia de optimizaciones de plataforma que Apple ha afinado durante años.

Un ScrollView con LazyVStack es ideal cuando necesitas layout personalizado: cards, bloques de contenido mixto, cabeceras especiales o un diseño tipo feed. “Lazy” significa que construye filas conforme aparecen en pantalla, pero no te da exactamente el mismo comportamiento que List en todos los casos. Con datasets muy grandes, eso puede suponer mayor uso de memoria y desplazamiento entrecortado en dispositivos antiguos.

Una regla simple de decisión:

  • Usa List para pantallas tipo tabla: ajustes, bandejas de entrada, órdenes, listas de administración
  • Usa ScrollView + LazyVStack para layouts personalizados y contenido mixto
  • Si tienes miles de ítems y sólo necesitas una tabla, empieza con List
  • Si necesitas control pixel-perfect, prueba LazyVStack y luego mide memoria y caídas de frames

También vigila estilos que silenciosamente ralentizan el scroll. Efectos por fila como sombras, blur y overlays complejos pueden forzar trabajo extra de render. Si quieres profundidad, aplica efectos pesados a elementos pequeños (como un icono) en lugar de toda la fila.

Ejemplo concreto: una pantalla de “Órdenes” con 5.000 filas a menudo se mantiene fluida en List porque las filas se reutilizan. Si cambias a LazyVStack y construyes filas tipo card con sombras grandes y múltiples overlays, puedes ver jank aunque el código parezca ordenado.

Paginación que se siente suave y evita picos de memoria

Construye más rápido que afinando listas a mano
Modela tus datos y paginación una vez, luego genera código iOS, web y backend juntos.
Probar AppMaster

La paginación mantiene las listas largas rápidas porque renderizas menos filas, mantienes menos modelos en memoria y le das a SwiftUI menos trabajo de diff.

Empieza con un contrato de paginación claro: un tamaño de página fijo (por ejemplo 30 a 60 items), una bandera de “no hay más resultados” y una fila de carga que sólo aparece mientras obtienes datos.

Una trampa común es disparar la siguiente página sólo cuando aparece la última fila. Eso suele llegar demasiado tarde y el usuario llega al final y ve una pausa. En su lugar, empieza a cargar cuando aparece una de las últimas filas.

Aquí hay un patrón simple:

@State private var items: [Item] = []
@State private var isLoading = false
@State private var reachedEnd = false

func loadNextPageIfNeeded(currentIndex: Int) {
    guard !isLoading, !reachedEnd else { return }
    let threshold = max(items.count - 5, 0)
    guard currentIndex >= threshold else { return }

    isLoading = true
    Task {
        let page = try await api.fetchPage(after: items.last?.id)
        await MainActor.run {
            let newUnique = page.filter { p in !items.contains(where: { $0.id == p.id }) }
            items.append(contentsOf: newUnique)
            reachedEnd = page.isEmpty
            isLoading = false
        }
    }
}

Esto evita problemas comunes como filas duplicadas (resultados API solapados), condiciones de carrera por múltiples llamadas onAppear y cargar demasiado a la vez.

Si tu lista soporta pull to refresh, resetea el estado de paginación con cuidado (limpia items, resetea reachedEnd, cancela tareas en vuelo si es posible). Si controlas el backend, IDs estables y paginación basada en cursor hacen la UI notablemente más suave.

Imágenes, texto y layout: mantén ligero el render de las filas

Convierte el desplazamiento fluido en un producto
Crea una app iOS con un backend real para que las actualizaciones de la lista sigan siendo predecibles a medida que los datos crezcan.
Comenzar a construir

Las listas largas rara vez se sienten lentas por culpa del contenedor. La mayor parte de las veces, es la fila. Las imágenes son la culpable habitual: decodificar, redimensionar y dibujar puede superar la velocidad del scroll, sobre todo en dispositivos antiguos.

Si cargas imágenes remotas, asegúrate de que el trabajo pesado no ocurra en el hilo principal durante el scroll. También evita descargar assets a resolución completa para una miniatura de 44–80 pt.

Ejemplo: una pantalla de “Mensajes” con avatares. Si cada fila descarga una imagen 2000x2000, la escala, y aplica blur o sombra, la lista se entrecortará aunque tu modelo de datos sea simple.

Mantén predecible el trabajo de imágenes

Hábitos de alto impacto:

  • Usa thumbnails generadas en el servidor cercanas al tamaño mostrado
  • Decodifica y redimensiona fuera del hilo principal cuando sea posible
  • Cachea thumbnails para que el scroll rápido no re-descargue ni re-decode
  • Usa un placeholder del mismo tamaño para prevenir parpadeos y saltos de layout
  • Evita modifiers caros en imágenes en filas (sombras pesadas, máscaras, blur)

Estabiliza el layout para evitar thrash

SwiftUI puede gastar más tiempo midiendo que dibujando si la altura de la fila sigue cambiando. Trata de mantener filas predecibles: frames fijos para thumbnails, límites de líneas consistentes y espaciado estable. Si el texto puede expandirse, tócCalo (por ejemplo, 1 a 2 líneas) para que una sola actualización no fuerce mediciones extra.

Los placeholders también importan. Un círculo gris que luego se convierte en avatar debería ocupar el mismo marco, para que la fila no refluya a mitad del scroll.

Cómo medir: comprobaciones de Instruments que revelan cuellos de botella reales

Trabajar en rendimiento es jugar a las conjeturas si sólo te guías por “se siente lento”. Instruments te dice qué corre en el main thread, qué se aloca durante un scroll rápido y qué causa frames perdidos.

Define una línea base en un dispositivo real (uno más antiguo si lo soportas). Haz una acción repetible: abre la pantalla, desplázate de arriba abajo rápido, activa cargar más una vez y vuelve a subir. Observa los puntos de mayor hitch, el pico de memoria y si la UI se mantiene responsiva.

Las tres vistas de Instruments que rinden frutos

Usa estas juntas:

  • Time Profiler: busca picos en el main-thread mientras desplazas. Layout, medición de texto, parseo JSON y decodificación de imágenes aquí suelen explicar los hitches.
  • Allocations: vigila los picos de objetos temporales durante un scroll rápido. Eso apunta a formateo repetido, nuevos attributed strings o reconstrucción de modelos por fila.
  • Core Animation: confirma frames perdidos y tiempos largos por frame. Esto ayuda a separar presión de renderizado de trabajo de datos lento.

Cuando encuentres un pico, entra en el árbol de llamadas y pregunta: ¿ocurre esto una vez por pantalla o una vez por fila, por cada desplazamiento? La segunda es la que rompe el desplazamiento fluido.

Añade signposts para eventos de scroll y paginación

Muchas apps hacen trabajo extra durante el scroll (cargas de imagen, paginación, filtrados). Los signposts te ayudan a ver esos momentos en la línea de tiempo.

import os
let log = OSLog(subsystem: "com.yourapp", category: "list")
os_signpost(.begin, log: log, name: "LoadMore")
// fetch next page
os_signpost(.end, log: log, name: "LoadMore")

Vuelve a probar después de cada cambio, uno a la vez. Si los FPS mejoran pero las Allocations empeoran, quizá intercambiaste stutter por presión de memoria. Conserva las notas de la línea base y mantén sólo cambios que mejoren los números relevantes.

Errores comunes que silenciosamente matan el rendimiento de listas

Elige cómo enviar y alojar
Despliega en AppMaster Cloud, AWS, Azure, Google Cloud, o exporta el código para autoalojarlo.
Explorar AppMaster

Algunos problemas son obvios (imágenes grandes, datasets enormes). Otros sólo se ven cuando los datos crecen, sobre todo en dispositivos antiguos.

1) IDs de fila inestables

Un error clásico es crear IDs dentro de la vista, como id: \\.self para tipos por referencia, o UUID() en el cuerpo de la fila. SwiftUI usa identidad para diffear actualizaciones. Si el ID cambia, SwiftUI trata la fila como nueva, la reconstruye y puede tirar cachés de layout.

Usa un ID estable de tu modelo (clave primaria, ID del servidor o un UUID guardado creado una vez al crear el item). Si no tienes uno, añádelo.

2) Trabajo pesado dentro de onAppear

onAppear se ejecuta más de lo que la gente espera porque las filas entran y salen de la pantalla al hacer scroll. Si cada fila inicia decodificación de imagen, parseo JSON o una búsqueda de base de datos en onAppear, obtendrás picos repetidos.

Saca el trabajo pesado de la fila. Precalcula lo que puedas cuando carguen los datos, cachea resultados y mantén onAppear limitado a acciones baratas (como disparar paginación cuando estés cerca del final).

3) Enlazar toda la lista a las ediciones de una fila

Cuando cada fila recibe un @Binding hacia un array grande, una pequeña edición puede parecer un cambio grande. Eso puede causar que muchas filas se re-evalúen y a veces que la lista entera se refresque.

Prefiere pasar valores inmutables a la fila y enviar cambios con una acción ligera (por ejemplo, “toggle favorito para id”). Mantén el estado por-fila dentro de la fila sólo cuando realmente pertenezca allí.

4) Demasiada animación durante el scroll

Las animaciones son caras en una lista porque pueden disparar pases de layout extra. Aplicar animation(.default, value:) en lo alto (en toda la lista) o animar cada pequeño cambio de estado puede hacer que el scroll se sienta pegajoso.

Mantenlo simple:

  • Limita animaciones a la fila que cambia
  • Evita animar durante scroll rápido (especialmente selección/resaltado)
  • Ten cuidado con animaciones implícitas sobre valores que cambian con frecuencia
  • Prefiere transiciones simples en lugar de efectos combinados complejos

Un ejemplo real: una lista estilo chat donde cada fila inicia una petición de red en onAppear, usa UUID() para id y anima cambios de estado “visto”. Esa combinación crea churn constante de filas. Arreglar la identidad, cachear trabajo y limitar animaciones suele hacer que la misma UI se sienta inmediatamente más fluida.

Lista de verificación rápida, un ejemplo simple y próximos pasos

Si sólo vas a hacer unas pocas cosas, empieza aquí:

  • Usa un id estable y único para cada fila (no el índice del array, no un UUID recién generado)
  • Mantén el trabajo de la fila pequeño: evita formateos pesados, árboles de vista grandes y propiedades computadas costosas en body
  • Controla las publicaciones: no permitas que estado de alta frecuencia (timers, tipeo, progreso) invalide toda la lista
  • Carga en páginas y prefetch para que la memoria se mantenga plana
  • Mide antes y después con Instruments para no adivinar

Imagina una bandeja de soporte con 20.000 conversaciones. Cada fila muestra asunto, preview del último mensaje, timestamp, badge de no-leído y un avatar. Los usuarios pueden buscar y llegan mensajes nuevos mientras hacen scroll. La versión lenta suele hacer varias cosas a la vez: reconstruye filas en cada pulsación, mide texto con demasiada frecuencia y descarga demasiadas imágenes demasiado pronto.

Un plan práctico que no requiere destrozar tu base de código:

  • Línea base: graba un scroll corto y una sesión de búsqueda en Instruments (Time Profiler + Core Animation).
  • Arregla identidad: asegúrate de que tu modelo tenga un id real del servidor/base de datos y ForEach lo use consistentemente.
  • Añade paginación: empieza con las últimas 50–100 entradas y carga más cuando el usuario se acerque al final.
  • Optimiza imágenes: usa thumbnails más pequeñas, cachea resultados y evita decodificar en el hilo principal.
  • Re-mide: confirma menos pases de layout, menos actualizaciones de vista y tiempos por frame más estables en dispositivos antiguos.

Si estás construyendo un producto completo (app iOS más backend y panel web), también ayuda diseñar el modelo de datos y el contrato de paginación temprano. Plataformas como AppMaster (appmaster.io) están pensadas para ese flujo full-stack: puedes definir datos y lógica visualmente y aún así generar código fuente real que puedas desplegar o autoalojar.

FAQ

¿Cuál es la solución más rápida cuando mi lista SwiftUI se desplaza con saltos?

Empieza por arreglar la identidad de las filas. Usa un id estable desde tu modelo y evita generar IDs en la vista, porque cambiar los IDs obliga a SwiftUI a tratar las filas como nuevas y reconstruir mucho más de lo necesario.

¿SwiftUI es lento porque “vuelve a renderizar” demasiado?

Recalcular el body de una vista suele ser barato; lo caro es lo que ese recompute desencadena. Layouts pesados, medición de texto, decodificación de imágenes y reconstruir muchas filas por identidad inestable son lo que típicamente causa pérdida de frames.

¿Cómo elijo un `id` estable para `ForEach` y `List`?

No uses UUID() dentro de la fila ni confíes en índices de array si los datos pueden insertarse, eliminarse o reordenarse. Prefiere un ID del servidor/base de datos o un UUID guardado en el modelo cuando se crea, para que el ID permanezca igual entre actualizaciones.

¿Puede `id: \\.self` empeorar el rendimiento de la lista?

Sí puede empeorar el rendimiento, sobre todo si el hash del valor cambia cuando campos editables cambian; SwiftUI puede verlo como una fila diferente. Si necesitas Hashable, base el hash en un único identificador estable en lugar de propiedades editables como name o isSelected.

¿Qué debo evitar hacer dentro del `body` de una fila?

Saca el trabajo pesado fuera del body. Preformatea fechas y números, evita crear nuevos formatters por fila y no construyas grandes arrays derivados con map/filter dentro de la vista; calcula una vez en el modelo o view model y pasa valores pequeños listos para mostrar a la fila.

¿Por qué mi `onAppear` se dispara tan a menudo en una lista larga?

Porque onAppear se ejecuta a menudo al entrar y salir las filas de la pantalla durante el scroll. Si cada fila inicia trabajo pesado allí (decodificar imágenes, lecturas de base de datos, parseo), tendrás picos repetidos; mantén onAppear limitado a acciones baratas como disparar paginación cuando estés cerca del final.

¿Qué causa las “tormentas de actualizaciones” que hacen que el desplazamiento se sienta pegajoso?

Cualquier valor publicado de alta frecuencia compartido con la lista puede invalidarla repetidamente, aunque los datos de las filas no cambien. Mantén timers, estado de tipeo y progreso fuera del objeto principal que maneja la lista, debouncea búsquedas y divide grandes ObservableObject en varios más pequeños cuando sea necesario.

¿Cuándo debo usar `List` vs `LazyVStack` para conjuntos de datos grandes?

Usa List cuando tu UI sea tipo tabla (filas estándar, acciones de swipe, selección, separadores) y quieras optimizaciones del sistema. Usa ScrollView + LazyVStack cuando necesites layouts personalizados, pero mide memoria y caídas de frames porque es más fácil disparar trabajo extra de layout.

¿Cuál es un enfoque simple de paginación que se mantenga fluido?

Empieza a cargar antes de que aparezca la última fila, activando la carga cuando el usuario alcance un umbral cercano al final, y evita disparos duplicados. Mantén tamaños de página razonables, controla isLoading y reachedEnd, y deduplica resultados por IDs estables para prevenir filas duplicadas y diffs extra.

¿Cómo mido qué está ralentizando realmente mi lista SwiftUI?

Registra una línea base en un dispositivo real y usa Instruments para encontrar picos en el main thread y picos de asignaciones durante un scroll rápido. Time Profiler muestra qué bloquea el scroll, Allocations revela la churn por fila y Core Animation confirma frames perdidos para saber si el cuello de botella es renderizado o trabajo de datos.

Fácil de empezar
Crea algo sorprendente

Experimente con AppMaster con plan gratuito.
Cuando esté listo, puede elegir la suscripción adecuada.

Empieza