保存 ScrollViews 位置并稍后滚动回它(偏移到位置) [英] Save ScrollViews position and scroll back to it later (offset to position)

查看:32
本文介绍了保存 ScrollViews 位置并稍后滚动回它(偏移到位置)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我找到了一种使用 GeometryReaderPreferenceKey 保存 ScrollViews offset 的方法.

struct TestScrollBackView:查看{@State 私有变量存储:Int = 0@State private var current: [Int] = []var主体:一些视图{ScrollViewReader { 代理在虚拟堆栈{堆栈{按钮(商店"){//硬代码仅用于演示!!!storage = current.sorted()[1]//第一个被 LazyVStack 移出屏幕打印(!!存储\(存储)")}按钮(恢复"){proxy.scrollTo(存储,锚点:.top)打印([x] 恢复 \(存储)")}}分隔线()滚动视图{懒惰VStack {ForEach(0..<1000, id: \.self) { obj in文本(项目:\(对象)").onAppear {打印(">>添加\(obj)")current.append(obj)}.onDisappear {current.removeAll { $0 == obj }打印(<<删除\(obj)")}.id(对象)}}}}}}}

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.

解决方案

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

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)
                        }
                    }
                }
            }
        }
    }
}

这篇关于保存 ScrollViews 位置并稍后滚动回它(偏移到位置)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆