保存 ScrollViews 位置并稍后滚动回到它(位置偏移)
Save ScrollViews position and scroll back to it later (offset to position)
我找到了一种使用 GeometryReader
和 PreferenceKey
.
保存 ScrollViews offset 的方法
并且 ScrollViewReader
有一个方法 scrollTo
可以滚动到一组 position。
问题是,第一个方法保存了一个 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)
}
}
}
}
}
}
}
我找到了一种使用 GeometryReader
和 PreferenceKey
.
并且 ScrollViewReader
有一个方法 scrollTo
可以滚动到一组 position。
问题是,第一个方法保存了一个 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)
}
}
}
}
}
}
}