I'm looking for a tidy way to identify the current visible top row from a ScrollView. I would like the NavigationTitle to display the current row number. Is there a way to do it with ScrollViewReader or must I do something with GeometryReader? In the code below I would like myRowNumber to update as the user scrolls up and down. Thanks in advance.
struct ContentView: View {
let myArray: [Int] = [Int](1...100)
@State private var myRowNumber: Int = 50
var body: some View {
NavigationView {
ScrollView {
LazyVStack{
ScrollViewReader { proxy in
ForEach(myArray, id: \.self) { index in
Text("Row \(index)").id(index).font(.title)
}
.onAppear {
proxy.scrollTo(50, anchor: .top)
}
}
}
}
.navigationTitle("Current row = \(myRowNumber)")
}
}
}
UPDATE
If your deployment target is iOS 17 (or macOS 14, etc.) or later, you can use the
scrollPosition(id:)
modifier to track the top visible row of the scroll view.ORIGINAL
So you want something like this:
SwiftUI doesn't offer a direct way to read the top row, so we have to compute it using other tools.
We need to know each row's position relative to the top of the scroll view. That means two things: getting an
Anchor
for each row, and computing the y coordinate of thatAnchor
relative to the top of theScrollView
.We can collect the
Anchor
s using theanchorPreference
modifier, but first we need to create aPreferenceKey
type to manage the collection.To turn an
Anchor<CGPoint>
into an actualCGPoint
, we need aGeometryProxy
. Assuming we have a proxy, we want to pick the row with the smallest y coordinate from those rows with a y coordinate of at least zero.Now we need to wrap a
GeometryReader
around theScrollView
to get aGeometryProxy
, and use a.overlayPreferenceValue
modifier inside theGeometryReader
to get access to the collectedAnchor
s.