Home > Software design >  How do I animate a number in swift?
How do I animate a number in swift?

Time:08-08

I want to animate a number. The animation I want to achieve is going from 0 increasing all the way up to the current number (at high speed). In this project, the number is the number of steps a user has taken. Is there a way this can be achieved?

      LazyVStack{


            ForEach(steps, id: \.id) { step in

//Here is the number I want to be animated

                    Text("\(step.count)")
                        .font(.custom(customFont, size: 50))



                        Text("Steps")
                            .font(.custom(customFont, size: 25))
                            .multilineTextAlignment(.center)
        }
    }

I believe I have a function along the right lines, I just need to apply it! Here is the function:

   func addNumberWithRollingAnimation() {
            withAnimation {
                // Decide on the number of animation tasks
                let animationDuration = 1000 // milliseconds
                let tasks = min(abs(self.enteredNumber), 100)
                let taskDuration = (animationDuration / tasks)
                
                // add the remainder of our entered num from the steps
                total  = self.enteredNumber % tasks
                // For each task
                (0..<tasks).forEach { task in
                    // create the period of time when we want to update the number
                    // I chose to run the animation over a second
                    let updateTimeInterval = DispatchTimeInterval.milliseconds(task * taskDuration)
                    let deadline = DispatchTime.now()   updateTimeInterval
                    
                    // tell dispatch queue to run task after the deadline
                    DispatchQueue.main.asyncAfter(deadline: deadline) {
                        // Add piece of the entire entered number to our total
                        self.total  = Int(self.enteredNumber / tasks)
                    }
                }
            }
        }

CodePudding user response:

Here is a utility function called Timer.animateNumber() which takes a Binding<Int> to animate, a Binding<Bool> busy which indicates if the value is currently animating, and Int start value, an Int end value, and a Double duration in seconds.

To use it, you need to define an @State private var number: Int to animate, and @State private var busy: Bool to keep track of the animation's state. This can also be used to terminate the animation early by just setting busy to false. Pass in your start value, end value, and duration in seconds.

This demo shows two animated numbers. The first counts up from 1 to 10000 in 1 second. The second counts down from 20 to 0 in 20 seconds. The Stop All button can be used to stop both animations.

extension Timer {
    static func animateNumber(number: Binding<Int>, busy: Binding<Bool>, start: Int, end: Int, duration: Double = 1.0) {
        busy.wrappedValue = true
        let startTime = Date()
        Timer.scheduledTimer(withTimeInterval: 1/120, repeats: true) { timer in
            let now = Date()
            let interval = now.timeIntervalSince(startTime)
            if !busy.wrappedValue {
                timer.invalidate()
            }
            if interval >= duration {
                number.wrappedValue = end
                timer.invalidate()
                busy.wrappedValue = false
            } else {
                number.wrappedValue = start   Int(Double(end - start)*(interval/duration))
            }
        }
    }
}

struct ContentView: View {
    @State private var number: Int = 0
    @State private var number2: Int = 0
    @State private var busy: Bool = false
    @State private var busy2: Bool = false

    var body: some View {
        VStack(spacing: 20) {
            Text(String(number))
            Button("Go") {
                if !busy {
                    Timer.animateNumber(number: $number, busy: $busy, start: 1, end: 10000, duration: 1)
                }
            }
            Text(String(number2))
            Button("Go") {
                if !busy2 {
                    Timer.animateNumber(number: $number2, busy: $busy2, start: 20, end: 0, duration: 20)
                }
            }
            Button("Stop All") {
                busy = false
                busy2 = false
            }
        }
    }
}
  • Related