I'm needing to implement a scroll-to-top button for a ScrollView living in a NavigationStack, but when I call proxy.scrollTo() (with proxy being the ScrollViewProxy provided by ScrollViewReader), the jump to the top doesn't complete the expansion of the navigation title if it's called in a withAnimation{} block. If proxy.scrollTo() is not in a withAnimation{} block, the jump to top completes as expected. I'm providing simplified code that demonstrates the issue. (*This is not my project. Just a demo of the issue I'm seeing.)
I'm seeing the behavior with XCode 15.2 testing on iOS 16.4, 17.0.1, and 17.2. Device, simulator, and preview.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationStack() {
LazyVNavView()
.navigationTitle("THE LAZYVNAV")
}
}
}
struct LazyVNavView: View {
var body: some View {
ScrollViewReader { proxy in
ScrollView {
LazyVStack {
ForEach(0..<100) { rowIndex in
Text("Row " + String(rowIndex))
.id(rowIndex)
}
HStack {
Spacer()
Button("To Top: Animation") {
withAnimation{
proxy.scrollTo(0)
}
}
Spacer()
Button("To Top: No Animation") {
proxy.scrollTo(0)
}
Spacer()
}
}
}
}
}
}
#Preview {
ContentView()
}
The issue persists if I:
- designate an anchor for scrollTo()
- use a VStack instead of LazyVStack
- put the to-top button on an overlay
- put the button and list in a ZStack
- dispatch the scrollTo() call to the main thread
- move ScrollViewReader inside ScrollView
- implement a NavigationPath.
The code shown is the simplest way I could come up with to demonstrate the issue. Images of the results after jumping with and without animation attached.
withAnimation { proxy.scrollTo(0) } *using animation - yuck

proxy.scrollTo(0) *no animation - succeeds

.inlinewhen tapping to scroll with or without animation.