Alex M Alex M - 3 months ago 15
Objective-C Question

iOS - Changing duration on an indefinite animation

I have an overloaded UIImageView (animationView) where I add another UIImageView as a subview to be animated (blobView). I am doing an indefinite rotation animation, which works perfectly well when I first start it. What I wanted to achieve was to be able to change the rate of rotation at the runtime. I have this function in animationView:

-(void)startBlobAnimation:(float)deltaT
{
[UIView beginAnimations:@"Spinning" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDuration:deltaT];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationRepeatCount:FLT_MAX];

CGAffineTransform rotation = CGAffineTransformMakeRotation(-symmetryAngle);
blobView.transform = rotation;

// Commit the changes and perform the animation.
[UIView commitAnimations];
}


Calling it with different values of deltaT after the animation was first started doesn't have any effect. If I add
[wheelView.layer removeAllAnimations];
at the start of the function then it successfully stops the animation but doesn't restart it. I also tried using the block command to start animation, with the same result. I am totally baffled at this point. Could somebody explain what the problem is? Thanks!

Answer Source

After a long struggle I came up with a solution that seems to work perfectly and give a smooth transition between animations. Basically I just figure out the current angle of rotation and use it to restart the animation at a different rate. One critical point here is in the last line: you MUST have that anim.keyPath there - it can't be Nil (as learned from experience). That way the new animation replaces the old animation, I guess. Oh, and to make it more clear: symmetryAngle is a rotation that makes the object look the same, like 72 degrees for 5-fold symmetry.

-(void)startWheelsAnimation:(float)deltaT
{
    float startingAngle = 0.0;

    if(isAnimating) {
        // If animation is in progress then calculate startingAngle to
        // reflect the current angle of rotation
        CALayer *presLayer = (CALayer*)[blobView.layer presentationLayer];
        CATransform3D transform = [presLayer transform];
        startingAngle = atan2(transform.m12, transform.m11);
    }

    isAnimating = YES;

    // Restart the animation with different duration, and so that it starts
    // from the current angle of rotation
    CABasicAnimation * anim = [ CABasicAnimation animationWithKeyPath:@"transform.rotation.z" ] ;
    anim.duration = deltaT;
    anim.repeatCount = CGFLOAT_MAX;
    anim.fromValue = @(startingAngle);
    anim.toValue = @(startingAngle - symmetryAngle) ;
    [blobView.layer addAnimation:anim forKey:anim.keyPath];
}