Using LazyVStack and LazyHStack in SwiftUI

Daily Coding Tip 043

One thing that was pretty ambiguous in the first iteration of SwiftUI was whether the rows of a List are queued or not.

When you scroll on a UITableView, cells that leave the bottom or top of the screen are added to a queue, meaning that every cell in the table does not have to be stored in memory at once. When a cell is about to be scrolled into view, a method like func dequeueReusableCell(withIdentifier: String) -> UITableViewCell? is called. The cells are considered to be reusable, since they can be destroyed and recreated, and removing them from the queue is called dequeueing.

Anyway, it turns out that List does reuse cells. But if you want to use a ScrollView instead, you’re back to everything loading at once and not queuing when they leave the top or bottom of the screen. You might be okay with using List instead of a verticalScrollView, but what happens if you want to scroll horizontally?

If you try it, you’ll notice that List has no option to scroll horizontally.

struct ContentView: View {
@State var text = ""
var body: some View {
VStack {
WhatJustHappenedView(text: text)
ScrollView(.horizontal) {
MyLazyHStack(text: $text)
}
.frame(height: 50)
ScrollView(.vertical) {
MyLazyVStack(text: $text)
}
}
}
}
struct WhatJustHappenedView: View {
let text: String
@State var toggleIsOn = true
var body: some View {
Group {
Toggle(isOn: $toggleIsOn) {
Text("Show what just happened")
}
.padding(.top)
if toggleIsOn {
Text("What just happened?")
Text("\(text)")
}
}
.padding(.horizontal)
}
}
struct MyLazyHStack: View {
@Binding var text: String
var body: some View {
LazyHStack {
ForEach(0..<150, id: \.self) {
index in
Text("LazyHStack \(index)")
.onAppear {
text = "LazyHStack \(index) appeared"
print(text)
}
.onDisappear {
text = "LazyHStack \(index) disappeared"
print(text)
}
}
}
}
}
struct MyLazyVStack: View {
@Binding var text: String
var body: some View {
LazyVStack {
ForEach(0..<150, id: \.self) {
index in
Text("LazyVStack \(index)")
.onAppear {
text = "LazyVStack \(index) appeared"
print(text)
}
.onDisappear {
text = "LazyVStack \(index) disappeared"
print(text)
}
}
}
}
}

In my example, we have an aptly named WhatJustHappenedView, which prints the most recent queueing event. If the stacks weren’t lazy, every Text cell inside them would appear once at the beginning, and they would never disappear when they are queued.

Instead, we see the events that prove that our memory is being allocated dynamically and not all at once.


Get more Daily Coding Tips in your inbox!