How do I animate the width of a UIView with a Timer?

2.6k Views Asked by At

I have a view that's set to the width of my view and a timer that gets some point in the future (in the next 5 minutes). My goal is to animate the view's width from the view.width down to 0

I was trying to do

UIView.animate(withDuration: date.timeIntervalSinceNow, delay: 0, options: .curveEaseIn, animations: {
    self.countdownBar?.frame = CGRect(x: 0, y:self.view.frame.maxY - 3, width: 0, height: 3)
}, completion: nil)

But this was making the countdownBar animate from the top of the view down to it's maxY position - 3 rather than the width

Any help would be appreciated!

Edit: Timer code

self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.countdownToCartExpiration), userInfo: nil, repeats: true)

And in countdownToCartExpiration I do some UI changes at specific time intervals of minutes and seconds

1

There are 1 best solutions below

11
Ryan Poolos On

You should either use something like a timer or just a UIViewAnimation.

If you'd like to animate the width down to 0 over the course of 5 minutes, then you'd do something like this.

var finalFrame = countdownBar!.frame
finalFrame.size.width = 0

UIView.animate(withDuration: 300, animations: {
    self.countdownBar?.frame = finalFrame
})

This assumes the view is the full width of the screen and will animate it down to zero over 300 seconds. It also assumes you're not using AutoLayout to manage your layout. If you are using Autolayout then we need to adjust this to change a constraint rather than change the frame.

Update

So since you have a timer firing every second. We'll still want to animate likely. If the timer were firing say every 30th or 60th of a second then we could just adjust the frame and it would look smooth. But every second will look choppy.

func countdownToCartExpiration (timer: Timer) {

    guard let countdownBar = countdownBar else {
        return
    }

    // Assuming you're already tracking currentTime somehow
    currentTime += timer.timeInterval

    var finalFrame = countdownBar.frame
    let totalTime = 300.0

    finalFrame.size.width = view.frame.width * CGFloat(currentTime / totalTime)

    UIView.animate(withDuration: timer.timeInterval, delay: 0, options: [.beginFromCurrentState, .curveLinear], animations: {
        countdownBar.frame = finalFrame
    }, completion: nil)

    // ... Other stuff
}

. beginFromCurrentState is important incase animations happen to overlap. .curveLinear is important here because with animations playing back to back we want the speed to be constant.

I've made the assumption you're tracking currentTime and the totalTime for this animation is 300 seconds. But you could adjust obviously. The goal is to set the width to the percentage of currentTime out of totalTime.