ScrollView Performance in SwiftUI
Slow scroll views are one of the most noticeable performance problems in SwiftUI apps. Optimizing them can make a significant difference, especially when dealing with large datasets or complex layouts.
Understanding ScrollView Performance
ScrollView in SwiftUI doesn't have built-in view recycling like UITableView or UICollectionView. This means all views are created upfront, which can lead to performance issues with large datasets. However, LazyVStack and LazyHStack provide lazy loading capabilities.
Use LazyVStack and LazyHStack
For better performance, use lazy stacks instead of regular ones inside ScrollView:
import SwiftUI
struct PerformantScrollView: View {
let items = Array(1...1000)
var body: some View {
ScrollView {
// Good: LazyVStack creates views on demand
LazyVStack {
ForEach(items, id: \.self) { item in
ItemRow(number: item)
}
}
}
}
}
struct ItemRow: View {
let number: Int
var body: some View {
Text("Item \(number)")
.frame(height: 50)
.frame(maxWidth: .infinity)
.background(Color.blue.opacity(0.1))
.cornerRadius(8)
.padding(.horizontal)
}
}
Avoid Heavy Computations in Views
Move expensive operations outside the view body:
struct OptimizedItemView: View {
let item: Item
let processedData: String // Computed outside the view
var body: some View {
VStack {
Text(item.title)
Text(processedData)
}
}
}
// Instead of:
// var body: some View {
// Text(expensiveComputation()) // Called on every render — avoid this
// }
Use LazyVGrid and LazyHGrid for Grids
For grid layouts, use lazy grids:
struct GridView: View {
let items = Array(1...100)
let columns = [
GridItem(.adaptive(minimum: 100))
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns) {
ForEach(items, id: \.self) { item in
RoundedRectangle(cornerRadius: 10)
.fill(Color.blue)
.frame(height: 100)
.overlay(Text("\(item)"))
}
}
.padding()
}
}
}
Optimize Image Loading
Use AsyncImage or cached image loading for remote images:
struct ImageListView: View {
let imageUrls: [URL]
var body: some View {
ScrollView {
LazyVStack {
ForEach(imageUrls, id: \.self) { url in
AsyncImage(url: url) { image in
image.resizable()
} placeholder: {
Color.gray
}
.frame(height: 200)
}
}
}
}
}
Monitor Performance
Use Instruments to profile your scroll views:
-
Time Profiler to identify slow code
-
SwiftUI Profiler to see view updates
-
Memory profiler to check for leaks
Caveats
-
LazyVStack/LazyHStack don't support all modifiers the same way as regular stacks
-
Be careful with onAppear/onDisappear in lazy stacks as they're called when views enter/exit viewport
-
Complex view hierarchies still impact performance even with lazy loading
Wrap up
Optimizing ScrollView performance in SwiftUI involves using lazy stacks, minimizing expensive computations, and being mindful of view creation. These techniques will help you build smooth, responsive scrolling experiences.
Resources:
Read more
- SwiftLeeds 2024 (day 2) • 7 minutes reading time.
- AppDevCon 2025 • 5 minutes reading time.
- Swift Package: NetworkMonitor • 3 minutes reading time.
Share
Share Bluesky Mastodon Twitter LinkedIn Facebook