I want to create an animation similar to app opens in iPhone in iOS7. In this animation it just shows that app is opening from which point and closing at same point.
As far as I know, that animation is made using screenshots. It updates the frame of the view and simultaneously makes a smooth transition from the app logo to the screenshot from the app. I have imitated the opening of the iPod (music) application from the bottom right corner of the device to the screen size:
UIView * v = [[UIView alloc]init];
CGSize size = self.view.bounds.size;
CGRect frameInitial = CGRectMake(size.width - 30, size.height - 30, 20, 20);
CGRect frameFinal = CGRectMake(0,0, size.width, size.height);
[v setFrame:frameInitial];
Then use the lines below when you want to animate the frame size:
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
[v setFrame:frameFinal];
} completion:nil];
Edit: Did not realize that the zooming also included the background. The code below is not tested (I am not at work) so expect some defects and typos.
Imagine you have two layers on the view controller's view. Directly on the vc there is the app you want to be opened, lets call it finalView. And on the top layer there is the window with all apps, which will zoom and fade into your app, which is a view behind it. Lets call it firstView.
Initial cond: firstView has a frame of 320 x 480 (It is a window with all the app icons). It has an alpha of 1. finalView has the same frame and alpha, but it is behind firstView. Final cond: finalView will still have the same frame and alpha. But firstView will zoom into bottom right corner (will have a huge frame) and fade out (alpha -> 0).
//Initial cond: (Or better yet use IB)
CGRect frameInitial = CGRectMake(0,0, self.view.size.width, self.view.size;
CGRect frameFinal = CGRectMake(self.view.size.width * -4 ,self.view.size.height * -5, self.view.size.width * -5,self.view.size.width * -6);
[v setFrame:frameInitial];
Then use the lines below when you want to animate the frame size:
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
[v setFrame:frameFinal];
} completion:nil];
@property (weak, nonatomic) IBOutlet UIImageView *bg;
@property (weak, nonatomic) IBOutlet UIImageView *cal;
…
bool nowZoomed = NO;
CGRect iconPosition = {16,113,60,60}; // customize icon position
- (CGRect)zoomedRect // just a helper function, to get the new background screen size
{
float screenWidth = UIScreen.mainScreen.bounds.size.width;
float screenHeight = UIScreen.mainScreen.bounds.size.height;
float size = screenWidth / iconPosition.size.width;
float x = screenWidth/2 - (CGRectGetMidX(iconPosition) * size);
float y = screenHeight/2 - (CGRectGetMidY(iconPosition) * size);
return CGRectMake(x, y, screenWidth * size, screenHeight * size);
}
- (IBAction)test
{
float animationDuration = 0.3f; //default
if (nowZoomed) // zoom OUT
{
[UIView animateWithDuration:animationDuration animations:^{ // animate to original frame
_cal.frame = iconPosition;
_bg.frame = UIScreen.mainScreen.bounds;
} completion:^(BOOL finished) {
[UIView animateWithDuration:animationDuration/2.0f animations:^{ // then fade out
_cal.alpha = 0.0f;
} completion:^(BOOL finished) {
_cal.hidden = YES;
}];
}];
}
else // zoom IN
{
_cal.alpha = 0.0f;
_cal.hidden = NO;
[UIView animateWithDuration:animationDuration/2.0f animations:^{ // fade in faster
_cal.alpha = 1.0f;
}];
[UIView animateWithDuration:animationDuration animations:^{ // while expanding view
_cal.frame = UIScreen.mainScreen.bounds;
_bg.frame = [self zoomedRect];
}];
}
nowZoomed = !nowZoomed;
}
{16,113,60,60}
IBOutlet
for both (the very first two lines of code)-(void)test
the storyboard picture (left) and animation transition (right)
I have small repo that uses a UICollectionViewFloatLayout
to create the zoom effect, https://github.com/MichaelQuan/ios7ZoomEffect. It is still a work in progress but the basic idea is there
The layout code is:
@interface ExpandingCollectionViewLayout ()
@property (nonatomic, assign) CGRect selectedCellFrame;
@property (nonatomic, strong) NSIndexPath *selectedIndexPath;
@end
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray *layoutAttributes = [super layoutAttributesForElementsInRect:rect];
[layoutAttributes enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes *obj, NSUInteger idx, BOOL *stop) {
[self _transformLayoutAttributes:obj];
}];
return layoutAttributes;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes *layoutAttributes = [super layoutAttributesForItemAtIndexPath:indexPath];
[self _transformLayoutAttributes:layoutAttributes];
return layoutAttributes;
}
- (void)_transformLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
if (self.selectedIndexPath != nil)
{
if ([layoutAttributes.indexPath isEqual:self.selectedIndexPath]) {
// set the frame to be the bounds of the collectionView to expand to the entire collectionView
layoutAttributes.frame = self.collectionView.bounds;
} else {
//scale = collectionView.size / cell_selected.size
//translate = (scale - 1)(cell_i.center - cell_selected.center) + (collectionView.center - cell_selected.center)
CGRect collectionViewBounds = self.collectionView.bounds;
CGRect selectedFrame = self.selectedCellFrame;
CGRect notSelectedFrame = layoutAttributes.frame;
// Calculate the scale transform based on the ratio between the selected cell's frame and the collection views bound
// Scale on that because we want everything to look scaled by the same amount, and the scale is dependent on how much the selected cell has to expand
CGFloat x_scale = collectionViewBounds.size.width / selectedFrame.size.width;
CGFloat y_scale = collectionViewBounds.size.height / selectedFrame.size.height;
CGAffineTransform scaleTransform = CGAffineTransformMakeScale(x_scale, y_scale);
// Translation based on how much the selected cell has been scaled
// translate based on the (scale - 1) and delta between the centers
CGFloat x_zoomTranslate = (x_scale - 1) * (CGRectGetMidX(notSelectedFrame) - CGRectGetMidX(selectedFrame));
CGFloat y_zoomTranslate = (y_scale - 1) * (CGRectGetMidY(notSelectedFrame) - CGRectGetMidY(selectedFrame));
CGAffineTransform zoomTranslate = CGAffineTransformMakeTranslation(x_zoomTranslate, y_zoomTranslate); //Translation based on how much the cells are scaled
// Translation based on where the selected cells center is
// since we move the center of the selected cell when expanded to full screen, all other cells must move by that amount as well
CGFloat x_offsetTranslate = CGRectGetMidX(collectionViewBounds) - CGRectGetMidX(selectedFrame);
CGFloat y_offsetTranslate = CGRectGetMidY(collectionViewBounds) - CGRectGetMidY(selectedFrame);
CGAffineTransform offsetTranslate = CGAffineTransformMakeTranslation(x_offsetTranslate, y_offsetTranslate);
// multiply translations first
CGAffineTransform transform = CGAffineTransformConcat(zoomTranslate, offsetTranslate);
transform = CGAffineTransformConcat(scaleTransform, transform);
layoutAttributes.transform = transform;
}
}
}
To expand a cell using this layout code, set both the selectedCellFrame
and selectedIndexPath
to the cell you want expanded and call performBatchUpdates:completion:
on the collection view. To collapse set selectedCellFrame = CGRectNull
and selectedIndexPath = nil
and call performBatchUpdates:completion:
I personally prefer to use CGAffineTransformMakeScale()
and setting -[CALayer affineTransform]
in this case.
affineTransform is super easy to use and comes with a few nice, implicit benefits from Core Animation. Examples being that does things like handling of changing the frame's origin for you implicitly and making it really easy to reset back to the initial size if needed -- you never lost it in the first place!
[UIView animateWithDuration:0.3 animations:^{
view.layer.affineTransform = CGAffineTransformMakeScale(10.0, 10.0); // To make a view larger:
otherView.layer.affineTransform = CGAffineTransformMakeScale(0.0, 0.0); // to make a view smaller
}];
and
// To reset views back to their initial size after changing their sizes:
[UIView animateWithDuration:0.3 animations:^{
view.layer.affineTransform = CGAffineTransformIdentity;
otherView.layer.affineTransform = CGAffineTransformIdentity;
}];