iosquartz-corecabasicanimation

Custom circular transition between two views


I want to create an animation similar to the animation for changing inbetween different views in the Spy Mouse app. See this video for reference:

http://www.youtube.com/watch?v=ylFdl7W3Srw

screenshot of the circular animation

I am unable to do the same. My animation shows a rectangular region instead of a circular view.

CABasicAnimation *cornerRadiusAction = [CABasicAnimation animationWithKeyPath:@"cornerRadius"];    

cornerRadiusAction.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
cornerRadiusAction.duration = 5.0f;
cornerRadiusAction.toValue = [NSNumber numberWithFloat:self.view.bounds.size.height*2];
[self.view.layer addAnimation:cornerRadiusAction forKey:nil];

Solution

  • Code in action.

    enter image description here

    Explanation.

    You cannot achieve such an effect simply animating the cornerRadius. You need to use masking of the CALayer and animate change of the size of a circular mask. Code below does what you want. The animated GIF shows it in action.

    @property (weak, nonatomic) IBOutlet UIView *customView;
    
    - (IBAction)buttonTapped:(id)sender
    {
        // Set up the shape of the circle.
        CGFloat maskDiameter = sqrtf(powf(CGRectGetWidth(_customView.bounds), 2)
                                     + powf(CGRectGetHeight(_customView.bounds), 2));
    
        CAShapeLayer *mask = [CAShapeLayer layer];
    
        // Make a circular shape.
        mask.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0.0f,
                                                                       0.0f,
                                                                       maskDiameter,
                                                                       maskDiameter)
                                               cornerRadius:maskDiameter/2.0f].CGPath;
        // Center the shape in the view.
        mask.position = CGPointMake((CGRectGetWidth(_customView.bounds) - maskDiameter)/2,
                                    (CGRectGetHeight(_customView.bounds) - maskDiameter)/2);
    
        // Fill the circle.
        mask.fillColor = [UIColor blackColor].CGColor;
    
        // Add as a mask to the parent layer
        _customView.layer.mask = mask;
    
        // Animate.
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
        animation.duration = 5.0f;
        animation.fillMode = kCAFillModeForwards;
        animation.removedOnCompletion = NO;
    
        // Create new path.
        CGPathRef newPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(maskDiameter/2.0f,
                                                                               maskDiameter/2.0f,
                                                                               0.0f,
                                                                               0.0f)
                                                       cornerRadius:0.0f].CGPath;
        // Set start and end values.
        animation.fromValue = (id)mask.path;
        animation.toValue = (__bridge id)newPath;
    
        // Start the animaiton.
        [mask addAnimation:animation forKey:@"path"];
    
    }