List — контејнерско View у SwiftUI за приказ података у облику вертикално помичне листе, аналог UITableView-а у UIKit-у. Према Apple Developer Documentation, 2024, List подржава статичке и динамичке секције, превлачење, премештање редова и pull-to-refresh. За разлику од UITableView-а, List користи декларативни API заснован на SwiftUI и ForEach, аутоматски управљајући поновном употребом ћелија и перформансама при великом броју редова.
Главно
List — View које приказује секвенцу елемената у вертикално помичној листи. Појавио се у iOS 13 заједно са SwiftUI и представља основни начин приказа листи података, замењујући UITableView из UIKit-а. List аутоматски управља поновном употребом ћелија, померањем и перформансама.
List користи lazy-loading: ћелије се стварају током померања, а не све одједном. То га разликује од VStack-а са ForEach унутар ScrollView-а, где се све ћелије стварају при рендеровању. List такође пружа уграђену подршку за превлачење, pull-to-refresh, уређивање (брисање/премештање) и избор редова.
Према Apple WWDC 2021 (Session 10072), List у iOS 15+ је добио значајна побољшања перформанси захваљујући новом механизму diffing-а на нивоу колекција. То је учинило List ефикаснијим при ажурирању података, посебно за листе са стотинама редова.
Програмери често бирају између List-а и ScrollView-а са VStack-ом за приказ скупа View-ова. Кључна разлика: List користи поновну употребу ћелија (као UITableView), а ScrollView + VStack ствара све View-ове одједном. За листе са фиксним бројем елемената (до 20) разлика је занемарљива. За динамичке листе са 50+ редова List је пожељнији због перформанси.
Статички List — листа са фиксним бројем редова, задатих директно у телу List-а. Користи се за меније, подешавања и форме са познатим скупом елемената. Сваки ред се декларише експлицитно, без петљи или ForEach-а.
// Статичка листа (за меније и подешавања)
List {
Text("Профил")
Text("Подешавања")
Text("О апликацији")
}
// Динамичка листа (за податке)
struct UserList: View {
let users: [User]
var body: some View {
List(users) { user in
HStack {
Text(user.name)
Text(user.role)
.foregroundColor(.secondary)
}
}
}
}
Динамички List користи иницијализатор List(data:rowContent:) или ForEach унутар тела List-а. Прва опција је згодна када сваки ред одговара једном елементу података. Друга — када постоје секције или додатни елементи између података.
Идентификација (Identifiable): За динамичке листе елементи података морају бити у складу са протоколом Identifiable или у туплу data:id треба навести KeyPath до јединственог идентификатора. SwiftUI користи идентификаторе за праћење промена: додавања, брисања и премештања редова.
Section — View за груписање редова у List-у са заглављем и опционим footer-ом. Section прихвата header и footer као ViewBuilder, омогућавајући коришћење не само текста, већ и прилагођених View-ова за заглавља секција.
struct SettingsView: View {
var body: some View {
List {
Section(header: Text("Налог")) {
Text("Име")
Text("Е-пошта")
}
Section(header: Text("Обавештења")) {
Toggle("Push", isOn: $pushEnabled)
Toggle("Email", isOn: $emailEnabled)
}
}
.listStyle(.insetGrouped)
}
}
// Динамичке секције са ForEach
List {
ForEach(groupedData.keys.sorted(), id: \.self) { key in
Section(header: Text(key)) {
ForEach(groupedData[key]!) { item in
Text(item.title)
}
}
}
}
Стилови List-а: SwiftUI пружа неколико уграђених стилова кроз модификатор .listStyle(). .insetGrouped — стандардни за iOS Settings, .plain — минималистички, .inset — са увлачењима, .sidebar — за Sidebar на iPad-у.
Према SwiftUI Cookbook (2024), Section са динамичким секцијама и ForEach унутар је стандардни образац за груписање података у апликацијама са сложеном структуром. Кључно правило: не уносите Section унутар Section-а и не користите иницијализатор List(data:) заједно са Section-ом — користите ForEach унутар тела List-а.
.swipeActions(edge:allowsFullSwipe:content:) — модификатор за iOS 15+ који додаје превлачење на редове List-а. Омогућава приказ дугмади при превлачењу улево (подразумевано) или удесно, са различитим бојама и улогама (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("Обриши", role: .destructive) {
tasks.removeAll { $0.id == task.id }
}
}
.swipeActions(edge: .leading) {
Button(task.isDone ? "Поништи" : "Готово") {
task.isDone.toggle()
}
.tint(.green)
}
}
}
.refreshable {
// Асинхроно учитавање података
await loadTasks()
}
}
}
.refreshable — модификатор за iOS 15+ који додаје pull-to-refresh. Прихвата асинхроно затварање (async) које се извршава када корисник повуче листу надоле. SwiftUI аутоматски приказује индикатор учитавања. Након завршетка операције индикатор се скрива.
.onDelete и .onMove — модификатори за iOS 13+ који додају подршку за брисање и премештање редова. Да бисте их користили, умотајте податке у ForEach са Binding-ом или проследите затварања кроз .onDelete(perform:) на List или ForEach.
Перформансе List-а зависе од броја редова, сложености сваке ћелије и учесталости ажурирања података. SwiftUI користи lazy-loading и поновну употребу ћелија (слично UITableView.dequeueReusableCell), али додатне оптимизације могу бити потребне за листе са 500+ редова.
| Оптимизација | Опис | iOS верзија |
|---|---|---|
| Identifiable | Јединствени ID за сваки елемент | iOS 13+ |
| EquatableView | Избегава прецртавање при једнаким подацима | iOS 13+ |
| id(_:) | Принудно поновно стварање View-а при промени ID-а | iOS 13+ |
| .equatable() | Поређење строго по Equatable | iOS 15+ |
| Diffable data | Аутоматски diff при променама | iOS 15+ |
Проблем 1: честа ажурирања. Ако се подаци у листи често ажурирају (нпр. сваке секунде), List може прецртавати видљиве ћелије при свакој промени стања. Решење: користите структуре (value types) за податке — SwiftUI их упоређује по вредности и прецртава само измењене редове.
Проблем 2: тешке ћелије. Ако сваки ред садржи сложену хијерархију View-ова, слике и анимације, померање може бити споро. Решење: издвојите ћелије у засебне View-ове, користите EquatableView за спречавање непотребних прецртавања. Према SwiftUI Lab (2024), подела сложеног реда на подкомпоненте смањује време рендеровања за 30–50%.
Проблем 3: велики број редова. При 1000+ редова List и даље ради ефикасно захваљујући lazy-loading-у, али почетно учитавање може успорити због израчунавања layout-а. Решење: користите LazyVStack само за листе са истоврсним редовима где нису потребне функције List-а (превлачење, секције). За потпуно функционалне листе List остаје најбољи избор.
Често постављана питања
List — контејнерско View за приказ помичне листе података у SwiftUI. Аналог UITableView-а у UIKit-у са декларативним API-јем. Подржава секције, превлачење, pull-to-refresh, уређивање и прилагођавање кроз .listStyle().
List користи lazy-loading и поновну употребу ћелија — ћелије се стварају током померања. ScrollView + VStack ствара све View-ове одједном. За листе са 50+ редова List је пожељнији. За фиксне мале скупове (до 20 елемената) разлика је занемарљива.
Користите модификатор .refreshable (iOS 15+). Проследите асинхроно затварање са логиком ажурирања података. SwiftUI аутоматски приказује индикатор учитавања и скрива га након завршетка асинхроне операције.
Користите Section View са header-ом и опционим footer-ом. Унутар Section-а поставите редове листе. За динамичке секције користите ForEach са groupedData. Стил листе се подешава кроз .listStyle(.insetGrouped) за iOS-олики изглед.
Користите структуре (value types) за податке, издвојите сложене ћелије у засебне View-ове са EquatableView, избегавајте честа ажурирања стања у сваком реду. За листе са 1000+ редова размотрите LazyVStack ако нису потребне функције List-а.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође