I am doing something quite similar in one of my projects having a grid of album arts.
This is the approach I am taking. The key is to use CAAnimationGroup
.
1) The whole animation would include scaling, rotating and moving along a path all at the same time - first for the album art layer and then for the modal view layer.
2)Animate the album art layer by flipping it by 90 degrees, scaling a little bit and moving to a predestined location from its current location. At this point it will disappear(vertical to the screen).
3) Add the modal view. Scale and transform the modal view to be in the exact location that the album art is positioned in step 1.
4) Animate the modal view from this position by scaling, rotating and moving along a path to fill the screen.
5) Remove the modal view.
6) Present modal view with no animation.
7) The path chosen would normally add the center of the screen as a control point. But then that can be changed based on how you want the animation to appear.
Below is a function where you can see the use of animation group. Hope this helps you. I still haven't figured out how to avoid the clipping of the animation by the navigation bars and the tab bars though. :)
+ (void)animateWithCurrentView:(UIView *)currentView
{
#define kResizeKey @"bounds.size"
#define kPathMovement @"position"
#define kRotation @"transform"
#define kGroupAnimation @"subviewBeingAnimated"
#define kLayerAnimation @"animateLayer"
//flip the view by 180 degrees in its place first.
currentView.layer.transform = CATransform3DRotate(currentView.layer.transform,radians(180), 0, 1, 0);
//set the anchor point so that the view rotates on one of its sides.
currentView.layer.anchorPoint = CGPointMake(0.0, 0.5);
/**
* Set up scaling
*/
CABasicAnimation *resizeAnimation = [CABasicAnimation animationWithKeyPath:kResizeKey];
//we are going to fill the screen here. So 320,480
[resizeAnimation setToValue:[NSValue valueWithCGSize:CGSizeMake(320, 480)]];
resizeAnimation.fillMode = kCAFillModeForwards;
resizeAnimation.removedOnCompletion = NO;
/**
* Set up path movement
*/
UIBezierPath *movePath = [UIBezierPath bezierPath];
//the control point is now set to centre of the filled screen. Change this to make the path different.
CGPoint ctlPoint = CGPointMake(160.0, 240.0);
//This is the starting point of the animation. This should ideally be a function of the frame of the view to be animated. Hardcoded here.
[movePath moveToPoint:CGPointMake(320, 60)];
//The anchor point is going to end up here at the end of the animation.
[movePath addQuadCurveToPoint:CGPointMake(0, 240) controlPoint:ctlPoint];
CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:kPathMovement];
moveAnim.path = movePath.CGPath;
moveAnim.removedOnCompletion = YES;
/**
* Setup rotation animation
*/
CABasicAnimation* rotateAnimation = [CABasicAnimation animationWithKeyPath:kRotation];
//start from 180 degrees (done in 1st line)
CATransform3D fromTransform = CATransform3DMakeRotation(radians(180), 0, 1, 0);
//come back to 0 degrees
CATransform3D toTransform = CATransform3DMakeRotation(radians(0), 0, 1, 0);
//This is done to get some perspective.
CATransform3D persp1 = CATransform3DIdentity;
persp1.m34 = 1.0 / -3000;
fromTransform = CATransform3DConcat(fromTransform, persp1);
toTransform = CATransform3DConcat(toTransform,persp1);
rotateAnimation.toValue = [NSValue valueWithCATransform3D:toTransform];
rotateAnimation.fromValue = [NSValue valueWithCATransform3D:fromTransform];
//rotateAnimation.duration = 2;
rotateAnimation.fillMode = kCAFillModeForwards;
rotateAnimation.removedOnCompletion = NO;
/**
* Setup and add all animations to the group
*/
CAAnimationGroup *group = [CAAnimationGroup animation];
[group setAnimations:[NSArray arrayWithObjects:moveAnim,rotateAnimation, resizeAnimation, nil]];
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
group.duration = 0.7f;
group.delegate = self;
[group setValue:currentView forKey:kGroupAnimation];
/**
* ...and go
*/
[currentView.layer addAnimation:group forKey:kLayerAnimation];
}