简体   繁体   中英

How To Pause & Resume Animation - KDCircularProgress Circular Bar

I have amended some code I pulled from https://github.com/kaandedeoglu/KDCircularProgress

I have a small UIView which represents the circle progress timer. I have referenced it and also instantiated it to KDCircularProgress class.

I have managed to implement methods to start and reset the timer progress circular bar.

But I am having trouble restarting the circular progress bar when I pause the animation.

My code preamble:

import UIKit

class SomeTableViewController: UITableViewController {

//Circular progress vars
var currentCount = 1.0
let maxCount = 60.0

//Reference selected UIView & instantiate
@IBOutlet weak var circularProgressView: KDCircularProgress!

Start animation - 60 second animation:

 if currentCount != maxCount {
    currentCount += 1
    circularProgressView.animateToAngle(360, duration: 60, completion: nil)
    }

To stop and reset the animation:

currentCount = 0
circularProgressView.animateFromAngle(circularProgressView.angle, toAngle: 0, duration: 0.5, completion: nil)

To pause the animation:

circularProgressView.pauseAnimation()

How would I set up a method to restart the animation after the paused state?

Many thanks in advance for any clarification. It's my first animation, I have tried to resolve the matter myself, but cannot seem to find any syntax applicable to my particular case.

UPDATED SOLUTION:

Thanks to @Duncan C for putting me on the right path.

I solved my problem as follows ...

Since I initiated the counter's progress using currentCount += 1 I thought I would try to pause the counter with:

if currentCount != maxCount {
currentCount -= 1
circularProgressView.animateToAngle(360, duration: 60, completion: nil)
}

which I thought would have a 'net' effect on the counter (netting off counter +=1 and counter -=1 ) to effectively stop the counter. In theory this should being the counter's progress to zero, but it continued to count down.

So I reverted back to circularProgressView.pauseAnimation() to pause the circular counter animation.

To restart the animation after being paused, I had to amend the duration to represent the updated duration - ie the time at which the animation was paused.

I used a bit of a trick here and included a NSTimer - which I happened to have in my code anyway.

To restart the animation at the time of pause:

    if currentCount != maxCount {
        currentCount += 1

        circularProgressView.animateToAngle(360, duration: NSTimeInterval(swiftCounter), completion: nil)
    }

I couldn't figure out how to update my duration for the animated circular bar, but did know how to update time passed using an NSTimer - timer with the same duration and countdown speed. So I tagged the reference to the updated timer's value. Issue resolved ;)

My Code:

import UIKit

class SomeFunkyTableViewController: UITableViewController {

//Circular progress variables
var currentCount = 0.0
let maxCount = 60.0

@IBOutlet weak var circularProgressView: KDCircularProgress!


//Timer countdown vars
var swiftTimer = NSTimer()
var swiftCounter = 60



@IBOutlet weak var startButton: UIButton!
@IBOutlet weak var timerView: UIView!

@IBOutlet weak var timerLabel: UILabel!

@IBOutlet weak var startView: UIView!

override func viewDidLoad() {

    circularProgressView.angle = 0

    timerLabel.text = String(swiftCounter)


        super.viewDidLoad()


override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}


@IBAction func startButton(sender: AnyObject) {


    pauseBtn.alpha = 1.0

    playBtn.alpha = 1.0

    stopBtn.alpha = 1.0

    circularProgressView.hidden = false


    if currentCount != maxCount {
        currentCount += 1

        circularProgressView.animateToAngle(360, duration: 60, completion: nil)
    }

    startView.hidden = true
    timerView.hidden = false


    swiftTimer = NSTimer.scheduledTimerWithTimeInterval(1, target:self, selector: #selector(SomeFunkyTableViewController.updateCounter), userInfo: nil, repeats: true)



}


@IBOutlet weak var pauseBtn: UIButton!
@IBAction func pauseButton(sender: AnyObject) {

  circularProgressView.pauseAnimation()



    swiftTimer.invalidate()

    pauseBtn.alpha = 0.5

    playBtn.alpha = 1.0

    stopBtn.alpha = 1.0
}


@IBOutlet weak var playBtn: UIButton!
@IBAction func playButton(sender: AnyObject) {




    if currentCount != maxCount {
        currentCount += 1

        circularProgressView.animateToAngle(360, duration: NSTimeInterval(swiftCounter), completion: nil)
    }




    if !swiftTimer.valid {
        swiftTimer = NSTimer.scheduledTimerWithTimeInterval(1, target:self, selector: #selector(SomeFunkyTableViewController.updateCounter), userInfo: nil, repeats: true)
    }


    if swiftCounter == 0 {
        swiftTimer.invalidate()
    }


    pauseBtn.alpha = 1.0

    playBtn.alpha = 0.5

    stopBtn.alpha = 1.0

}


@IBOutlet weak var stopBtn: UIButton!
@IBAction func stopButton(sender: AnyObject) {

    currentCount = 0
    circularProgressView.animateFromAngle(circularProgressView.angle, toAngle: 0, duration: 0.5, completion: nil)

    circularProgressView.hidden = true

    timerView.hidden = true
    startView.hidden = false



    swiftTimer.invalidate()
    swiftCounter = 60
    timerLabel.text = String(swiftCounter)


    pauseBtn.alpha = 1.0

    playBtn.alpha = 1.0

    stopBtn.alpha = 0.5
}

func updateCounter() {

    swiftCounter -= 1
    timerLabel.text = String(swiftCounter)

    if swiftCounter == 0 {
        swiftTimer.invalidate()
    }
}


}

Side Note : I have two overlapping views - StartView and TimerView. One is hidden on view load, hence the hide/unhide references. And I dim buttons on press - play/pause/stop.

Pausing and resuming an animation takes special code. You might not be able to do it without modifying the library you are using.

The trick is to set the speed on the animation on the parent layer that's hosting the animation to 0 and record the time offset of the animation.

Here are a couple of methods (written in Objective-C) from one of my projects that pause and resume an animation:

- (void) pauseLayer: (CALayer *) theLayer
{
  CFTimeInterval mediaTime = CACurrentMediaTime();
  CFTimeInterval pausedTime = [theLayer convertTime: mediaTime fromLayer: nil];
  theLayer.speed = 0.0;
  theLayer.timeOffset = pausedTime;
}

//-----------------------------------------------------------------------------

- (void) removePauseForLayer: (CALayer *) theLayer;
{
  theLayer.speed = 1.0;
  theLayer.timeOffset = 0.0;
  theLayer.beginTime = 0.0;
}

//-----------------------------------------------------------------------------

- (void) resumeLayer: (CALayer *) theLayer;
{
  CFTimeInterval pausedTime = [theLayer timeOffset];
  [self removePauseForLayer: theLayer];
  CFTimeInterval mediaTime = CACurrentMediaTime();
  CFTimeInterval timeSincePause = 
    [theLayer convertTime: mediaTime fromLayer: nil] - pausedTime;
  theLayer.beginTime = timeSincePause;
}

Note that starting in iOS 10, there is a newer, better way to pause and resume UIView animations: UIViewPropertyAnimator .

I have a sample project (written in Swift) on Github that demonstrates this new UIViewPropertyAnimator class. Here's the link: UIViewPropertyAnimator-test

Below is an extract from the README from that project:

A UIViewPropertyAnimator allows you to easily create UIView-based animations that can be paused, reversed, and scrubbed back and forth.

A UIViewPropertyAnimator object takes a block of animations, very much like the older animate(withDuration:animations:) family of UIView class methods. However, a UIViewPropertyAnimator can be used to run mulitiple animation blocks.

There is built-in support for scrubbing an animation by setting the fractionComplete property on the animator. There is NOT an automatic mechanism to observe the animation progress, however.

You can reverse a UIViewPropertyAnimator animation by setting its isReversed property, but there are some quirks. If you change the isReversed property of a running animator from false to true, the animate reverses, but you can't set the isReversed property from true to false while the animation is running and have it switch direction from reverse to forward "live". You have to first pause the animation, switch the isReversed flag, and then restart the animation. (To use an automotive analogy, you can switch from forward to reverse while moving, but you have to come to a comlete stop before you can switch from reverse back into drive.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM