List — je kontejnerové View ve SwiftUI pro zobrazení dat ve formě vertikálně posouvatelného seznamu, obdoba UITableView v UIKit. Podle Apple Developer Documentation, 2024 List podporuje statické a dynamické sekce, akce přetažení, přetahování řádků a pull-to-refresh. Na rozdíl od UITableView používá List deklarativní API založené na SwiftUI a ForEach, automaticky spravující opětovné použití buněk a výkon při velkém počtu řádků.
Hlavní body
List — je View, které zobrazuje sekvenci prvků ve vertikálně posouvatelném seznamu. Objevilo se v iOS 13 spolu se SwiftUI a je hlavním způsobem zobrazování seznamů dat, nahrazující UITableView z UIKit. List automaticky spravuje opětovné použití buněk, posouvání a výkon.
List používá lazy-loading: buňky jsou vytvářeny při posouvání, ne všechny najednou. To jej odlišuje od VStack s ForEach uvnitř ScrollView, kde jsou všechny buňky vytvářeny při vykreslování. List také poskytuje vestavěnou podporu pro akce přetažení, pull-to-refresh, úpravy (smazání/přesun) a výběr řádků.
Podle Apple WWDC 2021 (Session 10072) získal List v iOS 15+ významná vylepšení výkonu díky novému mechanismu diffing na úrovni kolekcí. To učinilo List efektivnějším při aktualizaci dat, zejména pro seznamy se stovkami řádků.
Vývojáři často volí mezi List a ScrollView s VStack pro zobrazení sady Views. Klíčový rozdíl: List používá opětovné použití buněk (jako UITableView), zatímco ScrollView + VStack vytváří všechny Views najednou. Pro seznamy s pevným počtem prvků (do 20) je rozdíl zanedbatelný. Pro dynamické seznamy s 50+ řádky je List preferovaný z hlediska výkonu.
Statický List — je seznam s pevným počtem řádků, zadaných přímo v těle List. Používá se pro menu, nastavení a formuláře se známou sadou prvků. Každý řádek je deklarován explicitně, bez smyček nebo ForEach.
// Statický seznam (pro menu a nastavení)
List {
Text("Profil")
Text("Nastavení")
Text("O aplikaci")
}
// Dynamický seznam (pro data)
struct UserList: View {
let users: [User]
var body: some View {
List(users) { user in
HStack {
Text(user.name)
Text(user.role)
.foregroundColor(.secondary)
}
}
}
}
Dynamický List používá inicializátor List(data:rowContent:) nebo ForEach uvnitř těla List. První možnost je vhodná, když každý řádek odpovídá jednomu datovému prvku. Druhá — když jsou mezi daty sekce nebo další prvky.
Identifikace (Identifiable): Pro dynamické seznamy musí datové prvky odpovídat protokolu Identifiable nebo v n-tici data:id musí být zadán KeyPath k jedinečnému identifikátoru. SwiftUI používá identifikátory ke sledování změn: přidávání, mazání a přesouvání řádků.
Section — je View pro seskupení řádků v List s nadpisem a volitelnou patičkou. Section přijímá header a footer jako ViewBuilder, což umožňuje použít nejen text, ale také vlastní Views pro nadpisy sekcí.
struct SettingsView: View {
var body: some View {
List {
Section(header: Text("Účet")) {
Text("Jméno")
Text("E-mail")
}
Section(header: Text("Oznámení")) {
Toggle("Push", isOn: $pushEnabled)
Toggle("Email", isOn: $emailEnabled)
}
}
.listStyle(.insetGrouped)
}
}
// Dynamické sekce s ForEach
List {
ForEach(groupedData.keys.sorted(), id: \.self) { key in
Section(header: Text(key)) {
ForEach(groupedData[key]!) { item in
Text(item.title)
}
}
}
}
Styly List: SwiftUI poskytuje několik vestavěných stylů prostřednictvím modifikátoru .listStyle(). .insetGrouped — standardní pro iOS Settings, .plain — minimalistický, .inset — s odsazením, .sidebar — pro Sidebar na iPadu.
Podle SwiftUI Cookbook (2024) je Section s dynamickými sekcemi a ForEach uvnitř standardním vzorem pro seskupování dat v aplikacích se složitou strukturou. Klíčové pravidlo: nevkládejte Section do Section a nepoužívejte inicializátor List(data:) společně s Section — použijte ForEach uvnitř těla List.
.swipeActions(edge:allowsFullSwipe:content:) — modifikátor pro iOS 15+, který přidává akce přetažení k řádkům List. Umožňuje zobrazit tlačítka při přetažení doleva (výchozí) nebo doprava, s různými barvami a rolemi (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("Smazat", role: .destructive) {
tasks.removeAll { $0.id == task.id }
}
}
.swipeActions(edge: .leading) {
Button(task.isDone ? "Zpět" : "Hotovo") {
task.isDone.toggle()
}
.tint(.green)
}
}
}
.refreshable {
// Asynchronní načítání dat
await loadTasks()
}
}
}
.refreshable — modifikátor pro iOS 15+, který přidává pull-to-refresh. Přijímá asynchronní uzavření (async), které se spustí, když uživatel táhne seznam dolů. SwiftUI automaticky zobrazí indikátor načítání. Po dokončení operace je indikátor skryt.
.onDelete a .onMove — modifikátory pro iOS 13+, které přidávají podporu pro mazání a přesouvání řádků. Pro jejich použití obalte data v ForEach s Binding nebo předejte uzavření přes .onDelete(perform:) na List nebo ForEach.
Výkon List závisí na počtu řádků, složitosti každé buňky a frekvenci aktualizací dat. SwiftUI používá lazy-loading a opětovné použití buněk (podobně jako UITableView.dequeueReusableCell), ale další optimalizace mohou být potřebné pro seznamy s 500+ řádky.
| Optimalizace | Popis | Verze iOS |
|---|---|---|
| Identifiable | Jedinečné ID pro každý prvek | iOS 13+ |
| EquatableView | Zabraňuje překreslení při stejných datech | iOS 13+ |
| id(_:) | Vynucené znovuvytvoření View při změně ID | iOS 13+ |
| .equatable() | Porovnání striktně podle Equatable | iOS 15+ |
| Diffable data | Automatický diff při změnách | iOS 15+ |
Problém 1: časté aktualizace. Pokud jsou data v seznamu často aktualizována (např. každou sekundu), List může překreslovat viditelné buňky při každé změně stavu. Řešení: používejte struktury (value types) pro data — SwiftUI je porovnává podle hodnoty a překresluje pouze změněné řádky.
Problém 2: těžké buňky. Pokud každý řádek obsahuje složitou hierarchii Views, obrázky a animace, posouvání může být pomalé. Řešení: přesuňte buňky do samostatných Views, používejte EquatableView k zabránění zbytečnému překreslování. Podle SwiftUI Lab (2024) snižuje rozdělení složitého řádku na podkomponenty dobu vykreslování o 30–50%.
Problém 3: velký počet řádků. Při 1000+ řádcích List stále funguje efektivně díky lazy-loading, ale počáteční načítání se může zpomalit kvůli výpočtu layoutu. Řešení: používejte LazyVStack pouze pro seznamy s homogenními řádky, kde nejsou potřeba funkce List (přetažení, sekce). Pro plně funkční seznamy zůstává List nejlepší volbou.
Často kladené otázky
List — kontejnerové View pro zobrazení posouvatelného seznamu dat ve SwiftUI. Obdoba UITableView v UIKit s deklarativním API. Podporuje sekce, akce přetažení, pull-to-refresh, úpravy a přizpůsobení přes .listStyle().
List používá lazy-loading a opětovné použití buněk — buňky jsou vytvářeny při posouvání. ScrollView + VStack vytváří všechny Views najednou. Pro seznamy s 50+ řádky je List preferovaný. Pro pevné malé sady (do 20 prvků) je rozdíl zanedbatelný.
Použijte modifikátor .refreshable (iOS 15+). Předejte asynchronní uzavření s logikou aktualizace dat. SwiftUI automaticky zobrazí indikátor načítání a skryje jej po dokončení asynchronní operace.
Použijte Section View s nadpisem a volitelnou patičkou. Umístěte řádky seznamu dovnitř Section. Pro dynamické sekce použijte ForEach s groupedData. Styl seznamu se nastavuje přes .listStyle(.insetGrouped) pro vzhled podobný iOS.
Používejte struktury (value types) pro data, přesuňte složité buňky do samostatných Views s EquatableView, vyhýbejte se častým aktualizacím stavu v každém řádku. Pro seznamy s 1000+ řádky zvažte LazyVStack, pokud nejsou potřeba funkce List.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také