2

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

Zack Shapiro
  • 6,648
  • 17
  • 83
  • 151

1 Answers1

4

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.

Ryan Poolos
  • 18,421
  • 4
  • 65
  • 98
  • I already have a timer going, I'd rather use than than an `animate` block – Zack Shapiro Apr 18 '17 at 21:27
  • btw, `finalFrame.width = 0` throws the error "width is a get-only property" – Zack Shapiro Apr 18 '17 at 21:29
  • Oops yea you'd need `finalFrame.size.width`. I've updated it. As for the timer, could you post the timer you've setup and the method you're calling? if you'd like to use the timer to manually change the width, then we would need a method being called regularly during the timer. Is this for something like a progress bar? – Ryan Poolos Apr 18 '17 at 21:31
  • Thanks Ryan. Would you mind providing an answer with a Timer as well? That'd be super helpful. Appreciate your help! – Zack Shapiro Apr 18 '17 at 21:32
  • So if you're calling the timer every second, then you probably still want to use the animate block. If you were calling it more frequently we could just set the frame of the bar without an animate block. I'll adjust the answer. – Ryan Poolos Apr 18 '17 at 21:36
  • That makes sense. Unfortunately the code you posted above isn't working to animate the width. I've copied it exactly – Zack Shapiro Apr 18 '17 at 21:37
  • Alright I've added information about working with your timer. As for the width not working, are you setting up your countdownBar in Interface Builder and/or are you using AutoLayout to position it? – Ryan Poolos Apr 18 '17 at 21:49
  • Thanks Ryan. I think you're right, the `UIAnimation` route is a better way to go. I'm setting up the countdownBar laying it out in code where it has a height of 3 and the width of its superview (which is the width of the screen) – Zack Shapiro Apr 19 '17 at 01:28
  • Can you add the code that sets up your countdownBar? – Ryan Poolos Apr 19 '17 at 14:05
  • 1
    I got it to work, I ended up using a NSLayoutConstraint, setting the constant to `-countdownBar.width` and animating. Thanks again for your help Ryan, I appreciate it – Zack Shapiro Apr 19 '17 at 18:11
  • 1
    Yea can't touch the frame if you're using AutoLayout ;) – Ryan Poolos Apr 19 '17 at 19:51