当使用 ScrollViewReader 滚动到某个位置时,例如到宽度的 40% - 当 ScrollView 的内容调整大小时,如何保留滚动位置?
我写了一个小示例应用程序来说明问题:
最初,ScrollView 的 child 的宽度为 1600,滚动位置为 40%。当您单击“将宽度更改为 800”按钮时,子宽度更改为 800,并且 ScrollView 滚动到末尾。我希望 ScrollView 在调整大小后保留滚动位置,或者在调整大小后始终滚动到 40%。
struct ContentView: View {
@State private var relativeScrollPosition: Double?
@State private var childWidth: CGFloat = 1600
var body: some View {
VStack {
Button("Change width to 800") {
childWidth = 800
}
Button("Change width to 1600") {
childWidth = 1600
}
RelativeScrollView(
relativeScrollPosition: $relativeScrollPosition,
childWidth: childWidth
) {
HStack{
ZStack {
Spacer().frame(height: 100).background(Color.green)
Text("1")
}
ZStack {
Spacer().frame(height: 100).background(Color.green)
Text("2")
}
ZStack {
Spacer().frame(height: 100).background(Color.green)
Text("3")
}
ZStack {
Spacer().frame(height: 100).background(Color.green)
Text("4")
}
ZStack {
Spacer().frame(height: 100).background(Color.green)
Text("5")
}
ZStack {
Spacer().frame(height: 100).background(Color.green)
Text("6")
}
ZStack {
Spacer().frame(height: 100).background(Color.green)
Text("7")
}
ZStack {
Spacer().frame(height: 100).background(Color.green)
Text("8")
}
ZStack {
Spacer().frame(height: 100).background(Color.green)
Text("9")
}
ZStack {
Spacer().frame(height: 100).background(Color.green)
Text("10")
}
}
.frame(width: childWidth, height: 100, alignment: .center)
}
.onAppear {
scrollTo40percent()
}
.onChange(of: childWidth, perform: { _ in
scrollTo40percent()
})
}
}
private func scrollTo40percent() {
relativeScrollPosition = 0.4
}
}
struct RelativeScrollView<Content: View>: View {
@Binding var relativeScrollPosition: Double?
let childWidth: CGFloat
let isAnimating = true
var child: () -> Content
var body: some View {
ScrollViewReader { reader in
ScrollView(.horizontal) {
ZStack {
HStack {
// swiftlint:disable identifier_name
ForEach(0..<101) { i in
Spacer().id(i)
}
}
.frame(width: childWidth)
self.child()
}
}
.onAppear {
scroll(reader, to: relativeScrollPosition)
}
.onChange(of: relativeScrollPosition) { newPos in
scroll(reader, to: newPos)
}
}
}
private func scroll(_ reader: ScrollViewProxy, to position: Double?) {
guard let unwrappedPosition = position else { return }
assert(unwrappedPosition >= 0 && unwrappedPosition <= 1)
let elementToScrollTo = Int(unwrappedPosition * 100)
if isAnimating {
withAnimation {
reader.scrollTo(elementToScrollTo, anchor: .center)
}
} else {
reader.scrollTo(elementToScrollTo, anchor: .center)
}
}
}