5

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
enter image description here

proxy.scrollTo(0) *no animation - succeeds

enter image description here

3
  • I'm unable to produce this, even with all display modes. NavigationTitle always becomes .inline when tapping to scroll with or without animation. Commented Jan 16, 2024 at 3:45
  • Could not replicate your issue either, all works well for me in my tests. On MacOS 14.3, using Xcode 15.2, tested on real ios 17 devices (not Previews) and macCatalyst. It could be different on older systems. Commented Jan 16, 2024 at 5:33
  • 1
    I neglected to mention that I'm working with iOS. Currently seeing the behavior with XCode 15.2 testing on iOS 16.4, 17.0.1, and 17.2. Device, simulator, and preview. Commented Jan 16, 2024 at 20:10

1 Answer 1

3

I was able to replicate your issue and found a way to mitigate it. This is likely a bug and you should file a bug report to Apple I think. The way I "solved it" was to use the animation and then move to scroll view to zero with a delay without the animation:

Button("To Top: Animation") {
     withAnimation{
         proxy.scrollTo(0)
     }
     DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
          proxy.scrollTo(0)
     }
}

This way the animation completes and it is almost unnoticeable that the last part completes without the animation.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.