Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI: How to use withAnimation in a ScrollView?

I've been trying to animate a ScrollView using ScrollViewReader and withAnimation.

I can't figure out why these two animations are not working, either from Button or .onAppear?

import SwiftUI

struct ScrollView2: View {
    
    @State private var scrollText = false
    
    var body: some View {
        ScrollViewReader { scrollView in
            ScrollView {
                Button("Scroll to bottom") {
                    withAnimation(.linear(duration: 30)) {
                        scrollView.scrollTo(99, anchor: .center)
                    }
                }
                
                ForEach(0..<100) { index in
                    Text(String(index))
                        .id(index)
                }
                .onAppear(perform: {
                    withAnimation(.linear(duration: 30)) {
                        scrollView.scrollTo(scrollText ? 99 : 1, anchor: .center)
                    }
                    scrollText.toggle()
                })
            }
        }
    }
}
like image 246
jswallez Avatar asked Oct 15 '25 18:10

jswallez


1 Answers

Because duration doesn't seem to work with withAnimation yet, I had to be a bit hacky to get the animation effect I wanted.

Here's what I did:

  1. I added a ScrollViewReader to my ScrollView
  2. I used ForEach and added IDs to my items in my ScrollView
  3. Used an .offset and .animation modifiers to animate the ScrollView itself (not the items in it)
  4. Used .scrollTo within .onAppear to move at launch the ScrollView to an item further away from the start to allow the user to both scroll back and forward the items, even with the ScrollView being itself animated from right to left

Here's what my code looks like:

import SwiftUI
import AVKit

struct ProView: View {
    
    @State private var scrollText = false
    
    var body: some View {
        
        ZStack {
            
//            VStack {
//                Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1))
//                    .ignoresSafeArea(.all)
//            }
//            
//            VStack {
//                
//                VideoPlayer(player: AVPlayer(url: Bundle.main.url(forResource: "wave-1", withExtension: "mp4")!)) {
//                    VStack {
//                        Image("pro-text")
//                            .resizable()
//                            .frame(width: 200, height: .infinity)
//                            .scaledToFit()
//                    }
//                }
//                .ignoresSafeArea(.all)
//                .frame(width: .infinity, height: 300)
                
                ScrollView(.horizontal, showsIndicators: false) {
                    
                    ScrollViewReader { value in
                        
                        HStack(spacing: 5) {
                            
                            ForEach(0 ..< 100) { i in
                                
                                HStack {
                                    Image("benefit-1")
                                        .resizable()
                                        .frame(width: 120, height: 120)
                                    
                                    Image("benefit-2")
                                        .resizable()
                                        .frame(width: 120, height: 120)
                                    
                                    Image("benefit-3")
                                        .resizable()
                                        .frame(width: 120, height: 120)
                                    
                                    Image("benefit-4")
                                        .resizable()
                                        .frame(width: 120, height: 120)
                                    
                                    Image("benefit-5")
                                        .resizable()
                                        .frame(width: 120, height: 120)
                                    
                                    Image("benefit-6")
                                        .resizable()
                                        .frame(width: 120, height: 120)
                                    
                                    Image("benefit-7")
                                        .resizable()
                                        .frame(width: 120, height: 120)
                                    
                                    Image("benefit-8")
                                        .resizable()
                                        .frame(width: 120, height: 120)
                                    
                                    Image("benefit-9")
                                        .resizable()
                                        .frame(width: 120, height: 120)
                                    
                                    Image("benefit-10")
                                        .resizable()
                                        .frame(width: 120, height: 120)
                                }
                                .id(i)
                            }
                            
                        }
                        .offset(x: scrollText ? -10000 : 20)
                        .animation(Animation.linear(duration: 300).repeatForever(autoreverses: false))
                        .onAppear() {
                            value.scrollTo(50, anchor: .trailing)
                            scrollText.toggle()
                        }
                    }
                }
                
                Spacer()
            }
            
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ProView()
    }
}
like image 66
jswallez Avatar answered Oct 17 '25 08:10

jswallez



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!