1

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)
                    }
                }
            }
        }
2
  • 1
    You mean like this? dropbox.com/s/rmyv1v3czrwrdtb/animateNumber.mov?dl=0 Commented Aug 6, 2022 at 23:55
  • Actually it is not clear what kind of animation and under which conditions you want to achieve. Commented Aug 7, 2022 at 5:25

1 Answer 1

2

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
            }
        }
    }
}
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.