保存 ScrollViews 位置并稍后滚动回到它(位置偏移)

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

我找到了一种使用 GeometryReaderPreferenceKey.

保存 ScrollViews offset 的方法

并且 ScrollViewReader 有一个方法 scrollTo 可以滚动到一组 position

scrollTo

问题是,第一个方法保存了一个 offset,而第二个方法需要一个 position(或一个 id,即类似于我的情况)。如何将偏移量转换为 position/id 或者是否有任何其他方法来保存和加载 ScrollViews 位置?

这是我现在的代码,但它没有按我想要的方式滚动:

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: -[=10=].frame(in: .named("scroll")).origin.y)
        })
        .onPreferenceChange(ViewOffsetKey.self) { // save [=10=] }
    }
}.coordinateSpace(name: "scroll")

并且在视图的 onAppear 中我想将 scrollTarget 设置为保存的位置。但是它会滚动到任何地方,但不会滚动到我想要的位置。

我考虑过将偏移量除以一项的大小,但真的可以这样吗?听起来不太好。

在这种情况下您实际上不需要偏移量,只需存储当前可见视图的 ID(您可以使用任何适当的算法来确定如何检测它的数据),然后滚动到具有该 ID 的视图。

这是可能方法的简化演示。使用 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 { [=10=] == obj }
                                    print("<< removed \(obj)")
                                }.id(obj)
                        }
                    }
                }
            }
        }
    }
}