DAN
DAN

Reputation: 939

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.

Upvotes: 2

Views: 2327

Answers (2)

Duncan C
Duncan C

Reputation: 131426

If you want to animate changes in shape you could use a CAShapeLayer. That takes a CGPath that describes the shape to be drawn. It can only draw 1 path, and that drawing can only use a single line color and a single fill color. You can't draw some lines in color 1 and other lines in color 2, etc.

Once you have a CAShapeLayer you can animate changes to the CGPath object associated with the shape layer. The trick to making it work is that the starting and ending CGPath need to have the same number of control points. If you use a path that has 3 linked bezier paths, the end path needs to use 3 linked bezier paths also, but you can move the points around as much as you want.

I've found through experimentation that you can't animate changes to arcs in bezier curves. The reason is that internally, the system generates a series of bezier curves to create the arc, and there are more bezier segments the larger the arc angle.

Upvotes: 0

wkberg
wkberg

Reputation: 391

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 ;-)

Upvotes: 3

Related Questions