I have a ScrollView
that will support infinite scroll (it’s a calendar), and so I’m using a LazyVStack
for its contents.
I need to know at all times what the first visible item is. To be clear, as long as any of the view is visible — however slight — it should be marked as such. Less concerned with what feels like the first visible but what technically is.
I’ve tried depending on .onAppear
and .onDisappear
like in this basic example below:
struct RootView: View {
var body: some View {
ScrollView(.vertical) {
LazyVStack(spacing: .zero) {
ForEach(0..<100, id: \.self) { item in
VStack {
Text("\(item)")
}
.frame(height: 200)
.frame(maxWidth: .infinity)
.background(Color.random)
.onAppear {
print("\(item) appeared")
}
.onDisappear {
print("\(item) disappeared")
}
}
}
}.ignoresSafeArea()
}
extension Color {
/// Return a random color
static var random: Color {
return Color(
red: .random(in: 0...1),
green: .random(in: 0...1),
blue: .random(in: 0...1)
)
}
}
The issue with this approach is that there appears to be delays in these getting called. It can sometimes be immediate (i.e. when it should), but often it’s with a second or more delay. In a worst-case scenario, it’s never called if I scroll past the item too quickly. This isn’t any help because it’s important the UI updates as soon as the first visible item has changed. From my understanding, this is normal behaviour because there’s no guarantees that these get called as soon as a view becomes visible/hidden.
I’ve considered tracking the offset of each view and then checking with the scrollview’s but have been unable to identify a pattern when comparing numbers that would result in the correct item being identified as the first visible.
This seems like such a basic request but it doesn’t seem to have been answered by anyone (Get first visible index from LazyHStack in SwiftUI is close but I tried it and it’s buggy — depending on the scroll speed, some items’ visibility can be skipped entirely). Preferably the solution — if there is one — doesn’t involve a GeometryReader wrapper because considering the number of items, I fear it will be a major performance drain.