I have an SwiftUI app with a ScrollView. When I change the contents, while the ScrollView is scrolled a bit down, the scroll position is kept. I would like the that changes to data resets this scroll position, i.e. scroll to the top. I've included a small example of this behavior.
Scroll down a bit and press the "Toogle" text. Content changes but the position stays the same. How can I signal the ScrollView to 'reset' the scrolling?
struct ContentView: View
{
static let data1 = [
"Test 1","Test 2","Test 3","Test 4","Test 5",
"Test 6","Test 7","Test 8","Test 9","Test 10",
"Test 11","Test 12","Test 13","Test 14","Test 15",
"Test 16","Test 17","Test 18","Test 19","Test 20"
]
static let data2 = [
"Test 101","Test 102","Test 103","Test 104","Test 105",
"Test 106","Test 107","Test 108","Test 109","Test 110",
"Test 111","Test 112","Test 113","Test 114","Test 115",
"Test 116","Test 117","Test 118","Test 119","Test 120",
"Test 121","Test 122","Test 123","Test 124","Test 125"
]
@State var idx = 0
@State var data : [String] = data1
var body: some View
{
VStack
{
Text("Toggle")
.onTapGesture
{
self.idx = 1-self.idx
self.data = self.idx == 0 ? ContentView.data1 : ContentView.data2
}
ScrollView
{
VStack
{
ForEach(self.data,id:\.self)
{
item in
Text("\(self.idx) \(item)").padding(20)
}
}
.frame(width:400)
}
}
}
}
I found a workarround that seems to work, maybe not optimal or pretty. It needs more testing to verify that it works consistently.
Instead of changing the data variable in one step like this:
self.data = self.idx == 0 ? ContentView.data1 : ContentView.data2
I first empty the array and then set it, but with a little delay. That way the ScrollView empties out, reset the scroll position (as it's empty) and repopulates with the new data and scrolled to the top.
self.data = []
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01)
{
self.data = self.idx == 0 ? ContentView.data1 : ContentView.data2
}