List — är en container-View i SwiftUI för att visa data i form av en vertikalt rullningsbar lista, en analog till UITableView i UIKit. Enligt Apple Developer Documentation, 2024 stöder List statiska och dynamiska sektioner, svep-åtgärder, dra och släpp av rader och pull-to-refresh. Till skillnad från UITableView använder List ett deklarativt API baserat på SwiftUI och ForEach, och hanterar automatiskt återanvändning av celler och prestanda vid ett stort antal rader.
Huvudpunkter
List — är en View som visar en sekvens av element i en vertikalt rullningsbar lista. Den dök upp i iOS 13 tillsammans med SwiftUI och är det primära sättet att visa datalistor, och ersätter UITableView från UIKit. List hanterar automatiskt återanvändning av celler, rullning och prestanda.
List använder lazy-loading: celler skapas när du rullar, inte alla på en gång. Detta skiljer det från VStack med ForEach inuti ScrollView, där alla celler skapas vid rendering. List ger också inbyggt stöd för svep-åtgärder, pull-to-refresh, redigering (ta bort/flytta) och val av rader.
Enligt Apple WWDC 2021 (Session 10072) fick List i iOS 15+ betydande prestandaförbättringar tack vare en ny diffing-mekanism på samlingsnivå. Detta gjorde List mer effektiv vid uppdatering av data, särskilt för listor med hundratals rader.
Utvecklare väljer ofta mellan List och ScrollView med VStack för att visa en uppsättning vyer. Den viktigaste skillnaden: List använder cellåteranvändning (som UITableView), medan ScrollView + VStack skapar alla vyer på en gång. För listor med ett fast antal element (upp till 20) är skillnaden försumbar. För dynamiska listor med 50+ rader är att föredra på grund av prestanda.
Statisk List — är en lista med ett fast antal rader, angivna direkt i List-kroppen. Används för menyer, inställningar och formulär med en känd uppsättning element. Varje rad deklareras explicit, utan loopar eller ForEach.
// Statisk lista (för menyer och inställningar)
List {
Text("Profil")
Text("Inställningar")
Text("Om")
}
// Dynamisk lista (för data)
struct UserList: View {
let users: [User]
var body: some View {
List(users) { user in
HStack {
Text(user.name)
Text(user.role)
.foregroundColor(.secondary)
}
}
}
}
Dynamisk List använder initieraren List(data:rowContent:) eller ForEach inuti List-kroppen. Det första alternativet är bekvämt när varje rad motsvarar ett dataelement. Det andra — när det finns sektioner eller ytterligare element mellan data.
Identifiering (Identifiable): För dynamiska listor måste dataelement överensstämma med protokollet Identifiable eller i tupeln data:id måste en KeyPath till en unik identifierare anges. SwiftUI använder identifierare för att spåra ändringar: att lägga till, ta bort och flytta rader.
Section — är en vy för att gruppera rader i List med en rubrik och valfri sidfot. Section accepterar header och footer som ViewBuilder, vilket gör det möjligt att använda inte bara text utan även anpassade vyer för sektionsrubriker.
struct SettingsView: View {
var body: some View {
List {
Section(header: Text("Konto")) {
Text("Namn")
Text("E-post")
}
Section(header: Text("Notiser")) {
Toggle("Push", isOn: $pushEnabled)
Toggle("Email", isOn: $emailEnabled)
}
}
.listStyle(.insetGrouped)
}
}
// Dynamiska sektioner med ForEach
List {
ForEach(groupedData.keys.sorted(), id: \.self) { key in
Section(header: Text(key)) {
ForEach(groupedData[key]!) { item in
Text(item.title)
}
}
}
}
List-stilar: SwiftUI erbjuder flera inbyggda stilar via modifieraren .listStyle(). .insetGrouped — standard för iOS Settings, .plain — minimalistisk, .inset — med indrag, .sidebar — för Sidebar på iPad.
Enligt SwiftUI Cookbook (2024) är Section med dynamiska sektioner och ForEach inuti ett standardmönster för att gruppera data i applikationer med komplex struktur. Nyckelregel: placera inte Section inuti Section, och använd inte initieraren List(data:) tillsammans med Section — använd ForEach inuti List-kroppen.
.swipeActions(edge:allowsFullSwipe:content:) — modifierare för iOS 15+ som lägger till svep-åtgärder till List-rader. Gör det möjligt att visa knappar vid svep åt vänster (standard) eller höger, med olika färger och roller (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("Ta bort", role: .destructive) {
tasks.removeAll { $0.id == task.id }
}
}
.swipeActions(edge: .leading) {
Button(task.isDone ? "Ångra" : "Klar") {
task.isDone.toggle()
}
.tint(.green)
}
}
}
.refreshable {
// Asynkron dataladdning
await loadTasks()
}
}
}
.refreshable — modifierare för iOS 15+ som lägger till pull-to-refresh. Accepterar en asynkron closure (async) som körs när användaren drar ner listan. SwiftUI visar automatiskt laddningsindikatorn. När operationen är klar döljs indikatorn.
.onDelete och .onMove — modifierare för iOS 13+ som lägger till stöd för att ta bort och flytta rader. För att använda dem, omge data i ForEach med Binding eller skicka closures via .onDelete(perform:) på List eller ForEach.
List-prestanda beror på antalet rader, varje cells komplexitet och frekvensen av datauppdateringar. SwiftUI använder lazy-loading och cellåteranvändning (liknande UITableView.dequeueReusableCell), men ytterligare optimeringar kan behövas för listor med 500+ rader.
| Optimering | Beskrivning | iOS-version |
|---|---|---|
| Identifiable | Unikt ID för varje element | iOS 13+ |
| EquatableView | Undviker omrendering vid lika data | iOS 13+ |
| id(_:) | Tvingad återskapning av View vid ID-ändring | iOS 13+ |
| .equatable() | Strikt jämförelse enligt Equatable | iOS 15+ |
| Diffable data | Automatisk diff vid ändringar | iOS 15+ |
Problem 1: frekventa uppdateringar. Om data i listan uppdateras ofta (t.ex. varje sekund) kan List omrendera synliga celler vid varje tillståndsändring. Lösning: använd strukturer (value types) för data — SwiftUI jämför dem efter värde och omrenderar endast ändrade rader.
Problem 2: tunga celler. Om varje rad innehåller en komplex vyhierarki, bilder och animationer kan rullningen vara långsam. Lösning: flytta celler till separata vyer, använd EquatableView för att förhindra onödig omrendering. Enligt SwiftUI Lab (2024) minskar uppdelning av en komplex rad i underkomponenter renderingstiden med 30–50%.
Problem 3: stort antal rader. Vid 1000+ rader fungerar List fortfarande effektivt tack vare lazy-loading, men initial laddning kan sakta ner på grund av layoutberäkning. Lösning: använd LazyVStack endast för listor med homogena rader där List-funktioner (svep, sektioner) inte behövs. För fullt funktionella listor förblir List det bästa valet.
Vanliga frågor
List — en container-View för att visa en rullningsbar datalista i SwiftUI. Motsvarigheten till UITableView i UIKit med deklarativt API. Stöder sektioner, svep-åtgärder, pull-to-refresh, redigering och anpassning via .listStyle().
List använder lazy-loading och cellåteranvändning — celler skapas när du rullar. ScrollView + VStack skapar alla vyer på en gång. För listor med 50+ rader är List att föredra. För fasta små uppsättningar (upp till 20 element) är skillnaden försumbar.
Använd modifieraren .refreshable (iOS 15+). Skicka en asynkron closure med logik för datauppdatering. SwiftUI visar automatiskt laddningsindikatorn och döljer den efter att den asynkrona operationen slutförts.
Använd Section View med rubrik och valfri sidfot. Placera listraderna inuti Section. För dynamiska sektioner, använd ForEach med groupedData. Liststilen konfigureras via .listStyle(.insetGrouped) för ett iOS-liknande utseende.
Använd strukturer (value types) för data, flytta komplexa celler till separata vyer med EquatableView, undvik frekventa tillståndsuppdateringar i varje rad. För listor med 1000+ rader, överväg LazyVStack om List-funktioner inte behövs.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också