how to reset/restart an animation and have it appear continuous?

So, I am fairly new to iOS programming, and have inherited a project from a former coworker. We are building an app that contains a gauge UI. When data comes in, we want to smoothly rotate our "layer" (which is a needle image) from the current angle to a new target angle. Here is what we have, which worked well with slow data:



-(void) MoveNeedleToAngle:(float) target

{

static float old_Value = 0.0;

CABasicAnimation *rotateCurrentPressureTick = [CABasicAnimation animationWithKeyPath:@"transform.rotation");

[rotateCurrentPressureTick setDelegate:self];

rotateCurrentPressureTick.fromValue = [NSSNumber numberWithFloat:old_value/57.2958];

rotateCurrentPressureTick.removedOnCompletion=NO;

rotateCurrentPressureTick.fillMode=kCAFillModeForwards;

rotateCurrentPressureTick.toValue=[NSSNumber numberWithFloat:target/57.2958];

rotateCurrentPressureTick.duration=3; // constant 3 second sweep



[imageView_Needle.layer addAnimation:rotateCurrentPressureTick forKey:@"rotateTick"];



old_Value = target;



}




The problem is we have a new data scheme in which new data can come in (and the above method called) faster, before the animation is complete. What's happening I think is that the animation is restarted from the old target to the new target, which makes it very jumpy.



So I was wondering how to modify the above function to add a continuous/restartable behavior, as follows:



1. check if the current animation is in progress and

2. if so, figure out where the current animation angle is, and then

3. cancel the current and start a new animation from the current rotation to the new target rotation



Is it possible to build that behavior into the above function?



Thanks. Sorry if the question seems uninformed, I have studied and understand the above objects/methods, but am not an expert.

Answered by kristofarkas in 17525022

#EDIT

I am writing this on top because this is actually an even easier solution, though it has some drawbacks.

func moveNeedleToTarget(target: CGFloat) {
     UIView.animateWithDuration(3.0, delay: 0.0, usingSpringWithDamping: 10.0, initialSpringVelocity: 10.0, options: [], animations: { () -> Void in
                self.needleImageView.transform = CGAffineTransformMakeRotation(target)
            }, completion: nil)
}


This again takes care of everything. You could use the simple methods of UIView to animate, but this spring type animation is really cool.

#ENDEDIT


There is another, I think easier, way to solve your problem then additive animations.


The idea is that for your simple animation you can use an implicit one (if you don't know what this is then check the docs).

This takes care of restarting your animation whenever needed and keeping the model in sync etc.

Here's what you'll have to do (you might need to experiment a bit, as Core Animations are a bit tricky):


func moveNeedleToTarget(target: CGFloat) {

     var transform = CATransform3DIdentity
     //target should be in radians already here.
     transform = CATransform3DRotate(transform, target, 0, 0, 1)

     CATransaction.setAnimationDuration(3.0)
     needleImageLayer.transform = transform
}


Couple of things to keep in mind: make sure the anchor point of the layer is set correctly.

The adaptation you need to make to your code involves "additive animations" which deals with what you describe- updating an animation property before a previos one for the same property has finished. It mostly involves avoiding use of kCAFillModeForwards and removedOnCompletion=NO patterns to keep the animation presentation and models in synch. I don't have any links available at the moment, but it was a major thrust of system animations from ios 8 and was covered well in articles in obj.io.

Accepted Answer

#EDIT

I am writing this on top because this is actually an even easier solution, though it has some drawbacks.

func moveNeedleToTarget(target: CGFloat) {
     UIView.animateWithDuration(3.0, delay: 0.0, usingSpringWithDamping: 10.0, initialSpringVelocity: 10.0, options: [], animations: { () -> Void in
                self.needleImageView.transform = CGAffineTransformMakeRotation(target)
            }, completion: nil)
}


This again takes care of everything. You could use the simple methods of UIView to animate, but this spring type animation is really cool.

#ENDEDIT


There is another, I think easier, way to solve your problem then additive animations.


The idea is that for your simple animation you can use an implicit one (if you don't know what this is then check the docs).

This takes care of restarting your animation whenever needed and keeping the model in sync etc.

Here's what you'll have to do (you might need to experiment a bit, as Core Animations are a bit tricky):


func moveNeedleToTarget(target: CGFloat) {

     var transform = CATransform3DIdentity
     //target should be in radians already here.
     transform = CATransform3DRotate(transform, target, 0, 0, 1)

     CATransaction.setAnimationDuration(3.0)
     needleImageLayer.transform = transform
}


Couple of things to keep in mind: make sure the anchor point of the layer is set correctly.

how to reset/restart an animation and have it appear continuous?
 
 
Q