CS0521
CS0521

Reputation: 182

SwiftUI animations with delay

I have an array of 5 letters and I need to show each letter sliding in one at a time from left to right with 5 seconds between each, cycling in a continuous loop but showing no more than 5 letters.

Here's what I have so far, but I'm stuck. This doesn't seem like it should be too difficult...what am I missing? Thanks!

import SwiftUI

struct ContentView: View {
            
    @State private var letters = ["S","T","A","R","T"]
    @State private var timer = Timer.publish(every: 5, tolerance: 0.5, on: .main, in: .common).autoconnect()
    
    var body: some View {
        ZStack {

            HStack {
                ForEach(self.letters, id:\.self) { letter in
                    Text(letter)
                        .font(.custom("Menlo", size: 18))
                        .fontWeight(.black)
                        .frame(width: 38, height: 38, alignment: .center)
                        .background(Color.red)
                        .clipShape(Circle())
                        .foregroundColor(.white)
                        .shadow(radius: 10, x: 10, y: 10)
                        .transition(AnyTransition.slide)
                        .animation(Animation.linear(duration: 1).repeatCount(1))
                }
            }
        }
        .onReceive(timer) {_ in
            //????? should I use this? where and how?
        }
    }
}

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

Upvotes: 2

Views: 1168

Answers (1)

vacawama
vacawama

Reputation: 154523

In order to animate the characters onto the screen, they need to be offscreen first. That means you need to build up the array of letters your ForEach is showing.

I made the letters Identifiable giving them unique ids so that it wouldn't confuse the first T in START with the second T.

I'm using an offset for animation instead of slide. The arriving property of a letter is used to decide the direction of the offset.

enter image description here

struct Letter: Identifiable {
    let letter: String
    var arriving: Bool
    let id = UUID()
}

struct ContentView: View {
    @State private var start = [" ","S","T","A","R","T"].map { Letter(letter: $0, arriving: true) }
    @State private var letters = [Letter]()
    @State private var timer = Timer.publish(every: 2, tolerance: 0.5, on: .main, in: .common).autoconnect()
    
    var body: some View {
        ZStack {

            HStack {
                ForEach(self.letters) { letter in
                    Text(letter.letter)
                        .font(.custom("Menlo", size: 18))
                        .fontWeight(.black)
                        .frame(width: 38, height: 38, alignment: .center)
                        .background(Color.red)
                        .clipShape(Circle())
                        .foregroundColor(.white)
                        .shadow(radius: 10, x: 10, y: 10)
                        .transition(AnyTransition.offset(x: letter.arriving ? -250 : 250))
                        .animation(Animation.linear(duration: 1).repeatCount(1))
                }
            }
        }
        .onReceive(timer) {_ in
            print("TIMER")
        
            var letter = start.removeLast()
            letter.arriving = true
            letters.indices.forEach { idx in letters[idx].arriving = false }
            letters = [letter] + letters
            if letters.count > 5 {
                let last = letters.removeLast()
                start = [last] + start
            }
        }
    }
}

Upvotes: 2

Related Questions