List è una View contenitore in SwiftUI per visualizzare dati come un elenco scorrevole verticalmente, analogo a UITableView in UIKit. Secondo Apple Developer Documentation, 2024, List supporta sezioni statiche e dinamiche, azioni di scorrimento, riordinamento delle righe e pull-to-refresh. A differenza di UITableView, List utilizza un'API dichiarativa basata su SwiftUI e ForEach, gestendo automaticamente il riutilizzo delle celle e le prestazioni con un gran numero di righe.
Punti chiave
List è una View che visualizza una sequenza di elementi in un elenco scorrevole verticalmente. È stata introdotta in iOS 13 insieme a SwiftUI ed è il modo principale per visualizzare elenchi di dati, sostituendo UITableView di UIKit. List gestisce automaticamente il riutilizzo delle celle, lo scorrimento e le prestazioni.
List utilizza il lazy-loading: le celle vengono create man mano che si scorre, non tutte in una volta. Questo lo distingue da VStack con ForEach all'interno di ScrollView, dove tutte le celle vengono create al momento del rendering. List fornisce anche supporto integrato per azioni di scorrimento, pull-to-refresh, modifica (elimina/sposta) e selezione delle righe.
Secondo Apple WWDC 2021 (Session 10072), List in iOS 15+ ha ricevuto miglioramenti significativi delle prestazioni grazie a un nuovo meccanismo di diffing a livello di collezioni. Ciò ha reso List più efficiente nell'aggiornamento dei dati, specialmente per elenchi con centinaia di righe.
Gli sviluppatori spesso scelgono tra List e ScrollView con VStack per visualizzare un insieme di Views. La differenza principale: List utilizza il riutilizzo delle celle (come UITableView), mentre ScrollView + VStack crea tutte le Views immediatamente. Per elenchi di dimensioni fisse (fino a 20 elementi), la differenza è trascurabile. Per elenchi dinamici con 50+ righe, List è preferibile per le prestazioni.
Elenco statico è un elenco con un numero fisso di righe specificate direttamente nel corpo di List. Viene utilizzato per menu, impostazioni e moduli con un insieme noto di elementi. Ogni riga viene dichiarata esplicitamente, senza cicli o ForEach.
// Elenco statico (per menu e impostazioni)
List {
Text("Profilo")
Text("Impostazioni")
Text("Informazioni")
}
// Elenco dinamico (per dati)
struct UserList: View {
let users: [User]
var body: some View {
List(users) { user in
HStack {
Text(user.name)
Text(user.role)
.foregroundColor(.secondary)
}
}
}
}
Elenco dinamico utilizza l'inizializzatore List(data:rowContent:) o ForEach all'interno del corpo di List. Il primo approccio è conveniente quando ogni riga corrisponde a un elemento di dati. Il secondo è utile quando ci sono sezioni o elementi aggiuntivi tra i dati.
Identificazione (Identifiable): Per gli elenchi dinamici, gli elementi di dati devono conformarsi al protocollo Identifiable, oppure è necessario specificare un KeyPath per un identificatore univoco nella tupla data:id. SwiftUI utilizza gli identificatori per tracciare le modifiche: aggiunte, eliminazioni e spostamenti di righe.
Section è una View per raggruppare righe in una List con un'intestazione e un footer opzionale. Section accetta header e footer come ViewBuilder, consentendo di utilizzare non solo testo ma anche Views personalizzate per le intestazioni di sezione.
struct SettingsView: View {
var body: some View {
List {
Section(header: Text("Account")) {
Text("Nome")
Text("Email")
}
Section(header: Text("Notifiche")) {
Toggle("Push", isOn: $pushEnabled)
Toggle("Email", isOn: $emailEnabled)
}
}
.listStyle(.insetGrouped)
}
}
// Sezioni dinamiche con ForEach
List {
ForEach(groupedData.keys.sorted(), id: \.self) { key in
Section(header: Text(key)) {
ForEach(groupedData[key]!) { item in
Text(item.title)
}
}
}
}
Stili di List: SwiftUI fornisce diversi stili integrati tramite il modificatore .listStyle(). .insetGrouped — standard per iOS Settings, .plain — minimalista, .inset — con rientro, .sidebar — per Sidebar su iPad.
Secondo SwiftUI Cookbook (2024), Section con sezioni dinamiche e ForEach all'interno è un modello standard per raggruppare dati in applicazioni con struttura complessa. La regola principale: non annidare Section dentro Section e non utilizzare l'inizializzatore List(data:) insieme a Section — usa ForEach all'interno del corpo di List.
.swipeActions(edge:allowsFullSwipe:content:) — un modificatore per iOS 15+ che aggiunge azioni di scorrimento alle righe di List. Permette di visualizzare pulsanti quando si scorre a sinistra (predefinito) o a destra, con diversi colori e ruoli (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("Elimina", role: .destructive) {
tasks.removeAll { $0.id == task.id }
}
}
.swipeActions(edge: .leading) {
Button(task.isDone ? "Annulla" : "Fatto") {
task.isDone.toggle()
}
.tint(.green)
}
}
}
.refreshable {
// Caricamento dati asincrono
await loadTasks()
}
}
}
.refreshable — un modificatore per iOS 15+ che aggiunge pull-to-refresh. Accetta una closure asincrona che viene eseguita quando l'utente tira l'elenco verso il basso. SwiftUI visualizza automaticamente un indicatore di caricamento. Al termine dell'operazione, l'indicatore viene nascosto.
.onDelete e .onMove — modificatori per iOS 13+ che aggiungono supporto per l'eliminazione e il riordinamento delle righe. Per usarli, racchiudi i dati in ForEach con Binding o passa le closure tramite .onDelete(perform:) su List o ForEach.
Le prestazioni di List dipendono dal numero di righe, dalla complessità di ogni cella e dalla frequenza degli aggiornamenti dei dati. SwiftUI utilizza lazy-loading e riutilizzo delle celle (simile a UITableView.dequeueReusableCell), ma potrebbero essere necessarie ottimizzazioni aggiuntive per elenchi con 500+ righe.
| Ottimizzazione | Descrizione | Versione iOS |
|---|---|---|
| Identifiable | ID univoci per ogni elemento | iOS 13+ |
| EquatableView | Evita il ridisegno quando i dati sono uguali | iOS 13+ |
| id(_:) | Forza la ricreazione della View quando l'ID cambia | iOS 13+ |
| .equatable() | Confronto rigoroso tramite Equatable | iOS 15+ |
| Diffable data | Diff automatico sulle modifiche | iOS 15+ |
Problema 1: Aggiornamenti frequenti. Se i dati nell'elenco vengono aggiornati frequentemente (ad esempio, ogni secondo), List può ridisegnare le celle visibili a ogni cambio di stato. Soluzione: utilizza strutture (value types) per i dati — SwiftUI le confronta per valore e ridisegna solo le righe modificate.
Problema 2: Celle pesanti. Se ogni riga contiene una gerarchia di Views complessa, immagini e animazioni, lo scorrimento potrebbe rallentare. Soluzione: estrai le celle in Views separate, usa EquatableView per evitare ridisegni non necessari. Secondo SwiftUI Lab (2024), dividere una riga complessa in sottocomponenti riduce il tempo di rendering del 30–50%.
Problema 3: Grande numero di righe. Con 1000+ righe, List funziona ancora efficientemente grazie al lazy-loading, ma il caricamento iniziale potrebbe rallentare a causa del calcolo del layout. Soluzione: usa LazyVStack solo per elenchi con righe uniformi dove le funzionalità di List (scorrimento, sezioni) non sono necessarie. Per elenchi completi, List rimane la scelta migliore.
Domande frequenti
List è una View contenitore per visualizzare un elenco di dati scorrevole in SwiftUI. È l'equivalente di UITableView in UIKit con un'API dichiarativa. Supporta sezioni, azioni di scorrimento, pull-to-refresh, modifica e personalizzazione tramite .listStyle().
List utilizza lazy-loading e riutilizzo delle celle — le celle vengono create man mano che si scorre. ScrollView + VStack crea tutte le Views in una volta. Per elenchi con 50+ righe, List è preferibile. Per insiemi fissi piccoli (fino a 20 elementi), la differenza è trascurabile.
Usa il modificatore .refreshable (iOS 15+). Passa una closure asincrona con la logica di aggiornamento dei dati. SwiftUI visualizza automaticamente un indicatore di caricamento e lo nasconde dopo il completamento dell'operazione asincrona.
Usa Section View con un'intestazione e un footer opzionale. Posiziona le righe dell'elenco all'interno di Section. Per sezioni dinamiche, usa ForEach con groupedData. Lo stile dell'elenco viene configurato tramite .listStyle(.insetGrouped) per un aspetto simile a iOS.
Usa strutture (value types) per i dati, estrai le celle complesse in Views separate con EquatableView, evita aggiornamenti di stato frequenti in ogni riga. Per elenchi con 1000+ righe, considera LazyVStack se le funzionalità di List non sono necessarie.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche