简体   繁体   中英

SwiftUI: Horizontal ScrollView Animation + Gestures

I'm trying to recreate the Unfold App Subscription screen with an horizontal carousel of images that are moving automatically from right to left with enabled gestures to move right or left, and then the animation continues again.

Unfold App Subscription View

I found a way to animate an horizontal ScrollView using 3 modifiers:

  • offset
  • animation
  • onAppear

But when I'm dragging the carousel, I can't find a way to have the x coordinate changed to where I left.

If anyone has an idea how to solve that issue, that would be life-saving!

Here's the SwiftUI code I wrote:

import SwiftUI
import AVKit

struct ContentView: View {
    
    @State 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: 150, height: .infinity)
                            .scaledToFit()
                    }
                }
                .ignoresSafeArea(.all)
                .frame(width: .infinity, height: 300)
                
                ScrollView(.horizontal, showsIndicators: false) {
                    HStack(spacing: 5) {
                        
                        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)
                        
                    }
                    .offset(x: scrollText ? -500 : 20)
                    .animation(Animation.linear(duration: 30).repeatForever(autoreverses: false))
                    .onAppear() {
                        self.scrollText.toggle()
                    }
                }
                
                Spacer()
            }
            
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Sharing here the answer from my other post.

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()
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM