Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
302 views
in Technique[技术] by (71.8m points)

ios - Save ScrollViews position and scroll back to it later (offset to position)

I have found a way to save a ScrollViews offset with a GeometryReader and a PreferenceKey.

SwiftUI | Get current scroll position from ScrollView

And the ScrollViewReader has a method scrollTo to scroll to a set position.

scrollTo

The problem is, that the first one saves an offset while the second method expects a position (or an id, which is similar to the position in my case). How can I convert the offset to a position/id or is there any other way to save and load a ScrollViews position?

Here is the code I have now but it does not scroll the way I want:

ScrollView {
    ScrollViewReader { scrollView in
        LazyVGrid(columns: columns, spacing: 0) {
            ForEach(childObjects, id: .id) { obj in
                CustomView(obj: obj).id(obj.id)
            }
        }
        .onChange(of: scrollTarget) { target in
            if let target = target {
                scrollTarget = nil
                scrollView.scrollTo(target, anchor: .center)
            }
        }
        .background(GeometryReader {
            Color.clear.preference(key: ViewOffsetKey.self,
                value: -$0.frame(in: .named("scroll")).origin.y)
        })
        .onPreferenceChange(ViewOffsetKey.self) { // save $0 }
    }
}.coordinateSpace(name: "scroll")

And in the onAppear of the View I want to set scrollTarget to the saved position. But it scrolls anywhere but not to the position I want.

I thought about dividing the offset by the size of one item but is that really the way to go? It does not sound very good.

question from:https://stackoverflow.com/questions/65870891/save-scrollviews-position-and-scroll-back-to-it-later-offset-to-position

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You don't need actually offset in this scenario, just store id of currently visible view (you can use any appropriate algorithm for your data of how to detect it) and then scroll to view with that id.

Here is a simplified demo of possible approach. Tested with Xcode 12.1/iOS 14.1

demo

struct TestScrollBackView: View {
    @State private var stored: Int = 0
    @State private var current: [Int] = []
    
    var body: some View {
        ScrollViewReader { proxy in
            VStack {
                HStack {
                    Button("Store") {
                        // hard code is just for demo !!!
                        stored = current.sorted()[1] // 1st is out of screen by LazyVStack
                        print("!! stored (stored)")
                    }
                    Button("Restore") {
                        proxy.scrollTo(stored, anchor: .top)
                        print("[x] restored (stored)")
                    }
                }
                Divider()
                ScrollView {
                    LazyVStack {
                        ForEach(0..<1000, id: .self) { obj in
                            Text("Item: (obj)")
                                .onAppear {
                                    print(">> added (obj)")
                                    current.append(obj)
                                }
                                .onDisappear {
                                    current.removeAll { $0 == obj }
                                    print("<< removed (obj)")
                                }.id(obj)
                        }
                    }
                }
            }
        }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...