问题描述
我找到了一种使用 GeometryReader
和 PreferenceKey
保存 ScrollViews offset 的方法。
SwiftUI | Get current scroll position from ScrollView
并且 ScrollViewReader
有一个方法 scrollTo
可以滚动到设置的位置。
问题是,第一个方法保存了一个 offset 而第二个方法需要一个 position(或一个 id,在我的例子中类似于位置) .如何将偏移量转换为位置/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: -$0.frame(in: .named("scroll")).origin.y)
})
.onPreferenceChange(ViewOffsetKey.self) { // save $0 }
}
}.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 { $0 == obj }
print("<< removed \(obj)")
}.id(obj)
}
}
}
}
}
}
}