iOS Animations - Part 4

In this we are going to learn how to animate the movement of shape along the bezier path.

As we learned in the previous post, you can create a simple Sine wave shaped bezier path as follows,


UIBezierPath* circularPath = [UIBezierPath bezierPath];
[circularPath moveToPoint:CGPointMake(50, 300)];
[circularPath addCurveToPoint:CGPointMake(300, 300) controlPoint1:CGPointMake(100, 50) controlPoint2:CGPointMake(250, 450)];
CAShapeLayer* layerShape = [CAShapeLayer layer];
layerShape.lineWidth = 2.0f;
layerShape.fillColor = [UIColor clearColor].CGColor;
layerShape.strokeColor = [UIColor redColor].CGColor;
layerShape.path = circularPath.CGPath;
[self.view.layer addSublayer:layerShape];

This code will draw a bezier path which will look like this,

circular_bezier_path

Now we are going to place a shape on the viewport and animated it along this sinusoidal curve,

Now, create a shape layer which you want to animate along the path,


CALayer* layer = [CALayer layer];
layer.frame = CGRectMake(0, 0, 20, 20);
// Set the initial position for layer shape.
layer.position = CGPointMake(50, 300);
// Set the initial background color for layer shape
layer.backgroundColor = [UIColor greenColor].CGColor;
// Add layer to the view port
[self.view.layer addSublayer:layer];

This will look like this,

shape_on_the_bezier_path

Now to make things more interesting we will add 2 more types of animations in addition to our original movement animation.

  1. Movement
  2. Background color
  3. Rotation

We will then combine all 3 animations with CAAnimationGroup instance and then apply that animation group to target shape layer.


// Color change animation
CABasicAnimation* colorChangeAnimation = [CABasicAnimation animation];
colorChangeAnimation.keyPath = @"backgroundColor";
colorChangeAnimation.toValue = (__bridge id) [UIColor blueColor].CGColor;
    
// Usually use this approach for performing rotation 
while animating a shape
// Rotation animation
CABasicAnimation* rotationAnimation = [CABasicAnimation animation];
rotationAnimation.keyPath = @"transform.rotation";
rotationAnimation.byValue = @(M_PI*2);
    
// Position animation to animate shape while it is moving across bezier curve.
CAKeyframeAnimation* animation = [CAKeyframeAnimation animation];
animation.keyPath = @"position";
animation.path = circularPath.CGPath;
animation.rotationMode = kCAAnimationRotateAuto;

Now group all the animations using CAAnimationGroup object. When they are combined, all the animations added to CALayer instance are executed simultaneously.


CAAnimationGroup* animationGroup = [CAAnimationGroup animation];
animationGroup.animations = @[colorChangeAnimation, animation, rotationAnimation];
// Animation duration 
animationGroup.duration = 3.0f;
// Setup the delegate if you want to execute sequential operation when animation completes.
animationGroup.delegate = self;
// Set this parameter to YES if you want animation to re-execute in the backward style
animationGroup.autoreverses = NO;
// Set this parameter to YES is you want animation to get removed from underlying layer
animationGroup.removedOnCompletion = YES;
// Final position of layer when animation completes
layer.position = CGPointMake(300, 300);
// Animation key to uniquely identify given animation. If you are adding multiple animations to `CALayer`, make sure to add distinct keys for each of them.
[layer addAnimation:animationGroup forKey:@"multipleAnimationsKey"];

Now, since we have added code to animate the layer along bezier path, you can see smooth movement

bezier_path_movement_buggy

As you can see from the GIF when animation ends, the object reverts its color back to an original green color. However, we want to retain the final blue color.

This is where our animation delegate comes into play. Delegate will notify us as soon as animation is complete. You can then set the background color for layer to preserve the final value.


#pragma CAAnimationGroup delegate method
- (void)animationDidStop:(CAAnimationGroup *)animationGroup finished:(BOOL)flag {
    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    layer.backgroundColor = [UIColor blueColor].CGColor;
    [CATransaction commit];
}

It will solve the above mentioned bug with animating object,

bezier_curve_animation_smooth

Now the animation runs smoothly without any glitch. So overall we learned to add 3 types of animation to our CALayer instance. You may also add more animations based on the business requirement and designs.

You can find full source code along with demo and other animation examples on the GitHub Repository