Get position of scrollview

With the release of iOS 14, there is a new ScrollViewReader which seems like it can read the current position. However, the docs around this are minimal, and do not explain how to do so. So, what is ScrollViewReader, and how can I get the current position of a scrollview?
Answered by mtsrodrigues in 614420022
You can get the position by using GeometryReader to read the scrollView content frame, pass the value using PreferenceKey and observe it:

Code Block swiftstruct ContentView: View {    var body: some View {            ScrollView {                ZStack {                    LazyVStack {                        ForEach(0...100, id: \.self) { index in                            Text("Row \(index)")                        }                    }                    GeometryReader { proxy in                        let offset = proxy.frame(in: .named("scroll")).minY                        Color.clear.preference(key: ScrollViewOffsetPreferenceKey.self, value: offset)                    }                }            }            .coordinateSpace(name: "scroll")            .onPreferenceChange(ScrollViewOffsetPreferenceKey.self) { value in                print(value)            }    }}


The .frame(in: .named("scroll")) is required to get the frame relative to the ScrollView and obtain a 0 offset when at the top, without the height of the safe area. If you try using .frame(in: .global) you will get a non-zero value when the scroll view is at the top (20 or 44, depends on the device).


ScrollViewReader actually provides a ScrollViewProxy with a function scrollTo(_ id:, anchor:) that you can use to scroll to the position of a View related to a given id:

Code Block swiftstruct ContentView: View {    @State private var position = 0    var body: some View {        VStack {            HStack {                Button("Top") { position = 0 }                Button("Middle") { position = 500 }                Button("Bottom") { position = 1000 }            }            ScrollView {                ScrollViewReader { proxy in                    LazyVStack {                        ForEach(0...1000, id: \.self) { index in                            Text("Row \(index)")                        }                    }                    .onChange(of: position) { value in                        withAnimation {                            proxy.scrollTo(value, anchor: .center)                        }                    }                }            }        }    }}


I see, thank you. So there is no way to get the current scroll position?
Accepted Answer
You can get the position by using GeometryReader to read the scrollView content frame, pass the value using PreferenceKey and observe it:

Code Block swiftstruct ContentView: View {    var body: some View {            ScrollView {                ZStack {                    LazyVStack {                        ForEach(0...100, id: \.self) { index in                            Text("Row \(index)")                        }                    }                    GeometryReader { proxy in                        let offset = proxy.frame(in: .named("scroll")).minY                        Color.clear.preference(key: ScrollViewOffsetPreferenceKey.self, value: offset)                    }                }            }            .coordinateSpace(name: "scroll")            .onPreferenceChange(ScrollViewOffsetPreferenceKey.self) { value in                print(value)            }    }}


The .frame(in: .named("scroll")) is required to get the frame relative to the ScrollView and obtain a 0 offset when at the top, without the height of the safe area. If you try using .frame(in: .global) you will get a non-zero value when the scroll view is at the top (20 or 44, depends on the device).


So ScrollViewREADER is not able to read the current offset of the scrollview? That's weird...
link: https://stackoverflow.com/questions/62588015/swiftui-get-current-scroll-position-from-scrollview

Code Block struct DemoScrollViewOffsetView: View {    @State private var offset = CGFloat.zero    var body: some View {        ScrollView {            VStack {                ForEach(0..<100) { i in                    Text("Item \(i)").padding()                }            }.background(GeometryReader {                Color.clear.preference(key: ViewOffsetKey.self,                    value: -$0.frame(in: .named("scroll")).origin.y)            })            .onPreferenceChange(ViewOffsetKey.self) { print("offset >> \($0)") }        }.coordinateSpace(name: "scroll")    }}struct ViewOffsetKey: PreferenceKey {    typealias Value = CGFloat    static var defaultValue = CGFloat.zero    static func reduce(value: inout Value, nextValue: () -> Value) {        value += nextValue()    }}


a real, working example with a nice implementation is here:

https://swiftuirecipes.com/blog/swiftui-scrollview-scroll-offset

Get position of scrollview
 
 
Q