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 that Anchor relative to the top of the ScrollView.
We can collect the Anchors using the anchorPreference modifier, but first we need to create a PreferenceKey type to manage the collection.
struct AnchorsKey: PreferenceKey {
// Each key is a row index. The corresponding value is the
// .center anchor of that row.
typealias Value = [Int: Anchor<CGPoint>]
static var defaultValue: Value { [:] }
static func reduce(value: inout Value, nextValue: () -> Value) {
value.merge(nextValue()) { $1 }
}
}
To turn an Anchor<CGPoint> into an actual CGPoint, we need a GeometryProxy. 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.
private func topRow(of anchors: AnchorsKey.Value, in proxy: GeometryProxy) -> Int? {
var yBest = CGFloat.infinity
var answer: Int? = nil
for (row, anchor) in anchors {
let y = proxy[anchor].y
guard y >= 0, y < yBest else { continue }
answer = row
yBest = y
}
return answer
}
Now we need to wrap a GeometryReader around the ScrollView to get a GeometryProxy, and use a .overlayPreferenceValue modifier inside the GeometryReader to get access to the collected Anchors.
struct ContentView: View {
let myArray: [Int] = [Int](1...100)
@State private var myRowNumber: Int = 50
var body: some View {
NavigationView {
GeometryReader { proxy in
ScrollView {
LazyVStack{
ScrollViewReader { proxy in
ForEach(myArray, id: \.self) { index in
Text("Row \(index)").id(index).font(.title)
.anchorPreference(
key: AnchorsKey.self,
value: .center
) { [index: $0] }
}
.onAppear {
proxy.scrollTo(50, anchor: .top)
}
}
}
}
.overlayPreferenceValue(AnchorsKey.self) { anchors in
let i = topRow(of: anchors, in: proxy) ?? -1
Color.clear
.navigationTitle("Current row = \(i)")
}
}
}
}
}