SwiftUI: List или LazyVStack? 🤔SwipeActions для любых view?

Alex Kraev
2 min readMay 13, 2023

--

List или LazyVStack? Что лучше использовать и когда?

Зависит, конечно, от поставленной задачи. Например, если приложение на UIKit, и хотите подтянуть SwiftUI, то необходимо учитывать, что разделители у ячеек в List, если они не нужны, до iOS 15 убираются весьма костыльным способом:

.onAppear {
UITableView.appearance().separatorStyle = .none
}

Тем самым можно зааффектить дизайн UITableView во всем приложении. В iOS 15 для этого уже появился специальный модификатор .listRowSeparator(.hidden) .

Одной из главных особенностей является рендеринг у List и LazyVStack, который отличается в зависимости от версий iOS. В iOS < 15 List рендерит ячейки “с запасом”, а именно, если List растянут на весь экран, то ленивой загрузки нет у первых 15 (iPhone 6s) — 20 элементов, при этом без разницы, какая высота фрейма у элемента. Напротив, LazyVStack рендерит только те элементы, которые видны на экране. Таким образом, например, вешая модификатор .onAppear{..} , мы получаем ожидаемое поведение только у LazyVStack.

Демонстрационный код доступен здесь

Начиная с iOS 15, List рендерит уже только элементы, видимые на экране. Получается, что пагинацию на List для iOS < 15 можно сделать только, если загружать > 20 элементов. Это не всем может подойти, в приоритете окажется LazyVStack.

Другим отличием является то, что ячейки List слабо кастомизируются. Однако у List есть большое преимущество в виде swipeActions. Но этот метод доступен, только начиная с iOS 15. Но можно ли как-то использовать всю мощь swipeActions для ячеек LazyVStack? Да! И не только для LazyVStack, а для любых вью! Добавьте себе в проект этот package! Кроме того, вы можете использовать его в проекте, ориентированном на iOS 13. Краткий мануал здесь.

Процесс добавления SwipeActions интуитивно просто, поэтому просто приведу пример кода:

import SwipeActions

struct YourView: View {

var body: some View {
ScrollView {
LazyVStack {
ForEach(1...100, id: \.self) { cell in
Text("Cell \(cell)")
.frame(height: 50, alignment: .center)
.frame(maxWidth: .infinity)
.contentShape(Rectangle())
.addSwipeAction(edge: .trailing) { // 👈🏻 магия здесь 🙂
Button {
print("remove \(cell)")
} label: {
Image(systemName: "trash")
.foregroundColor(.white)
}
.frame(width: 60, height: 50, alignment: .center)
.contentShape(Rectangle())
.background(Color.red)

Button {
print("Inform \(cell)")
} label: {
Image(systemName: "bell.slash.fill")
.foregroundColor(.white)
}
.frame(width: 60, height: 50, alignment: .center)
.background(Color.blue)

}
}
}
}
}
}

Результатом будет:

На этом все!✌🏻

Не забывайте подписываться на github и на канал в телеграмме, посвященный мобильной разработке.

--

--

Alex Kraev

Mobile engineer | IT lead | Writer | Open Source author from Moscow, like Swift, SwiftUI, Kotlin, Jetpack, subscribe to my channel: https://t.me/swiftui_dev