List — какво е, компонент за списък в SwiftUI

Автор: IT Sectr Публикувано: 2026-06-25 Време за четене: 7 мин

List — контейнерно View в SwiftUI за показване на данни под формата на вертикално превъртащ се списък, аналог на UITableView в UIKit. Според Apple Developer Documentation, 2024, List поддържа статични и динамични секции, действия за плъзгане, преместване на редове и pull-to-refresh. За разлика от UITableView, List използва декларативен API, базиран на SwiftUI и ForEach, като автоматично управлява повторното използване на клетки и производителността при голям брой редове.

Основни точки

  • List — контейнер за превъртащ се списък с данни в SwiftUI
  • ForEach — основният начин за динамично показване на данни в List
  • Секции — Section View за групиране на редове със заглавия
  • Действия за плъзгане — swipeActions за iOS 15+
  • Pull-to-refresh — .refreshable за iOS 15+

Какво е List в SwiftUI?

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

Разработчиците често избират между List и ScrollView с VStack за показване на набор от Views. Ключовата разлика: List използва повторно използване на клетки (като UITableView), докато ScrollView + VStack създава всички Views наведнъж. За списъци с фиксиран брой елементи (до 20) разликата е незначителна. За динамични списъци с 50+ редове List е за предпочитане от гледна точка на производителност.

Статични и динамични списъци

Статичен List — списък с фиксиран брой редове, зададени директно в тялото на List. Използва се за менюта, настройки и формуляри с известен набор от елементи. Всеки ред се декларира изрично, без цикли или ForEach.

swift
// Статичен списък (за менюта и настройки)
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, позволявайки използването не само на текст, но и на персонализирани Views за заглавия на секции.

swift
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.

Действия за плъзгане и pull-to-refresh

.swipeActions(edge:allowsFullSwipe:content:) — модификатор за iOS 15+, който добавя действия за плъзгане към редовете на List. Позволява показване на бутони при плъзгане наляво (по подразбиране) или надясно, с различни цветове и роли (destructive, cancel).

swift
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 и оптимизация

Производителността на List зависи от броя редове, сложността на всяка клетка и честотата на актуализации на данни. SwiftUI използва lazy-loading и повторно използване на клетки (подобно на UITableView.dequeueReusableCell), но допълнителни оптимизации може да са необходими за списъци с 500+ редове.

ОптимизацияОписаниеiOS версия
IdentifiableУникално ID за всеки елементiOS 13+
EquatableViewИзбягва пререндиране при равни данниiOS 13+
id(_:)Принудително пресъздаване на View при промяна на IDiOS 13+
.equatable()Сравнение стриктно по EquatableiOS 15+
Diffable dataАвтоматичен diff при промениiOS 15+

Проблем 1: чести актуализации. Ако данните в списъка се актуализират често (например всяка секунда), List може да пререндира видимите клетки при всяка промяна на състоянието. Решение: използвайте структури (value types) за данни — SwiftUI ги сравнява по стойност и пререндира само променените редове.

Проблем 2: тежки клетки. Ако всеки ред съдържа сложна йерархия от Views, изображения и анимации, превъртането може да бъде бавно. Решение: преместете клетките в отделни Views, използвайте EquatableView за предотвратяване на ненужно пререндиране. Според SwiftUI Lab (2024), разделянето на сложен ред на подкомпоненти намалява времето за рендиране с 30–50%.

Проблем 3: голям брой редове. При 1000+ редове List все още работи ефективно благодарение на lazy-loading, но първоначалното зареждане може да се забави поради изчисляване на layout. Решение: използвайте LazyVStack само за списъци с еднотипни редове, където не са необходими функциите на List (плъзгане, секции). За напълно функционални списъци List остава най-добрият избор.

Често задавани въпроси

Какво е List в SwiftUI?

List — контейнерно View за показване на превъртащ се списък с данни в SwiftUI. Аналог на UITableView в UIKit с декларативен API. Поддържа секции, действия за плъзгане, pull-to-refresh, редактиране и персонализиране чрез .listStyle().

Как се различава List от ScrollView + VStack?

List използва lazy-loading и повторно използване на клетки — клетките се създават при превъртане. ScrollView + VStack създава всички Views наведнъж. За списъци с 50+ редове List е за предпочитане. За фиксирани малки набори (до 20 елемента) разликата е незначителна.

Как да добавя pull-to-refresh в List?

Използвайте модификатора .refreshable (iOS 15+). Предайте асинхронно затваряне с логика за актуализиране на данни. SwiftUI автоматично показва индикатор за зареждане и го скрива след завършване на асинхронната операция.

Как да групирам редове в List?

Използвайте Section View с header и опционален footer. Поставете редовете на списъка вътре в Section. За динамични секции използвайте ForEach с groupedData. Стилът на списъка се конфигурира чрез .listStyle(.insetGrouped) за iOS-подобен вид.

Как да ускоря List с голям брой редове?

Използвайте структури (value types) за данни, преместете сложните клетки в отделни Views с EquatableView, избягвайте чести актуализации на състоянието във всеки ред. За списъци с 1000+ редове обмислете LazyVStack, ако не са необходими функциите на List.

Обобщение

  • List — контейнер за превъртащ се списък с lazy-loading и повторно използване на клетки
  • ForEach — основният начин за динамично показване на данни в List
  • Section — групиране на редове със заглавия и footer
  • swipeActions — действия за плъзгане за iOS 15+
  • refreshable — pull-to-refresh за iOS 15+
  • Стилове — insetGrouped, plain, inset, sidebar чрез .listStyle()
  • Производителност — използвайте Identifiable, EquatableView и value types

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също