Search code examples
iosuiviewcore-animationcalayer

Animating UIView shape and its content


In an iPhone app how would you animate the shape of an UIView, for example changing from a rectangle into a circle?

I've tried with:

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
animation.duration = 20.0;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.fromValue = (__bridge_transfer id)aPath;
animation.toValue = (__bridge_transfer id)anotherPath;
[myShapeLayer addAnimation:animation forKey:@"animatePath"];

where myShapeLayer is an instance of CAShapeLayer and aPath and anotherPath CGMutablePathRef. It works but the view content is not animated as well.

I need to transform a view into a circle and then let it shrink until it disappears.


Solution

  • Try something like this: Where animeView is your UIView

    CABasicAnimation *anim1 = [CABasicAnimation animationWithKeyPath:@"cornerRadius"];
    anim1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    anim1.fromValue = [NSNumber numberWithFloat:0.0f];
    anim1.toValue = [NSNumber numberWithFloat:50.0f]; //Half the size of your UIView
    anim1.duration = 2.0;
    [animeView.layer addAnimation:anim1 forKey:@"cornerRadius"];
    
    [UIView animateWithDuration:10.0 delay:2 options:UIViewAnimationOptionCurveEaseInOut animations:^{
    
        animeView.layer.cornerRadius = 50; //Half the size of your UIView
        CGRect reduceRect = animeView.frame;
        reduceRect.size.height = 0;
        reduceRect.size.width = 0;
        [animeView setFrame:reduceRect];
        animeView.alpha = 0;
                        } completion:nil];
    

    Might need some tweaks for you here and there ;-)

    EDIT 1:

    Ok so how about using two UIView animations? The first will shrink, strech and move your view. The second will shrink, slink and remove your view.

    [UIView animateWithDuration:2.0 delay:0.5 options:UIViewAnimationOptionCurveEaseIn animations:^{
    
        CGRect moveRect = animeView.frame;
        moveRect.origin.x = 0;
        moveRect.origin.y = (animeView.center.y -20); //Half the size of height reduction
        moveRect.size.height = (animeView.bounds.size.height -40); // height reduction
        moveRect.size.width = (animeView.bounds.size.width +20);
        [animeView setFrame:moveRect];
    
    } completion:^(BOOL finished){
        [UIView animateWithDuration:2.0 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
            CGRect reduceRect = animeView.frame;
            reduceRect.size.height = 0;
            reduceRect.size.width = 0;
            reduceRect.origin.x = -50;
            reduceRect.origin.y = animeView.center.y;
            animeView.alpha = 0;
            [animeView setFrame:reduceRect];
        } completion:nil];
    
    }];
    

    EDIT 2:

    A answer to you question in the comments:

    You can execute animations simultaneous by creating a CAAnimationGroup. Also I'm using a image to create the resize of content effect. Example:

    //Create a screenshot of your UIView... Still animeView in this example
    UIGraphicsBeginImageContext(animeView.bounds.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [animeView.layer renderInContext:context];
    UIImage *screenShot = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    //Add the image as subview:
    UIImageView * imageView = [[UIImageView alloc] initWithImage:screenShot];
    [animeView addSubview:imageView];
    
    //A cornerRadius animation:
    CABasicAnimation *radiusAni = [CABasicAnimation animationWithKeyPath:@"cornerRadius"];
    radiusAni.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    radiusAni.fromValue = [NSNumber numberWithFloat:0.0f];
    radiusAni.toValue = [NSNumber numberWithFloat:50.0f];
    
    //A stretch animation:
    CABasicAnimation *stretchAni = [CABasicAnimation animationWithKeyPath:@"transform.scale.x"];
    stretchAni.fromValue = [NSNumber numberWithDouble:1];
    stretchAni.toValue = [NSNumber numberWithDouble:(animeView.frame.size.width+100)/animeView.frame.size.width];
    
    //A slide animation:
    CABasicAnimation *slideAni = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];
    slideAni.fromValue = [NSNumber numberWithDouble:0];
    slideAni.toValue = [NSNumber numberWithDouble:-100];
    
    //A opacity animation:
    CABasicAnimation *opacityAni = [CABasicAnimation animationWithKeyPath:@"opacity"];
    opacityAni.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    opacityAni.fromValue = [NSNumber numberWithFloat:1];
    opacityAni.toValue = [NSNumber numberWithFloat:0];
    
    //The animationgroup
    CAAnimationGroup *animGroup = [CAAnimationGroup animation];
    
    //Add them to the group:
    [animGroup setAnimations:[NSArray arrayWithObjects:radiusAni, opacityAni, slideAni, stretchAni, nil]];
    //Set the properties:
    [animGroup setDuration:3.0];
    [animGroup setRemovedOnCompletion:NO];
    [animGroup setFillMode:kCAFillModeForwards];
    
    //Execute all the animations in the group:
    [animeView.layer addAnimation:animGroup forKey:nil];
    

    Then you'll have 4 animations executing at the same time and the resize of the content when stretching, shrinking or whatever you plan to do ;-)