Reputation: 29468
I'm trying to replicate the zooming in and fading of a book animation that happens on the iPad. Basically what happens if you haven't seen is a book is on a shelf, when tapped, it'll grow towards the center of the screen. When it nears the center of the screen, it cross fades with the page of the book you are on, whether that be the cover or page 100. On the iPhone, the cover of the book grows towards the center of the screen, then halfway through the transition, it fades into the last page you were on in the book, and the zoom finishes taking up the whole screen.
What I tried to do in order to replicate this is, someone clicks on my collectionView. I get the frame for that cell and take a screenshot of that cell. I create a layer with the contents being that image.
CALayer *smallLayer = [CALayer layer];
UIImage *smallImage = [UIImage renderImageFromView:self.smallZoomView];
smallLayer.frame = self.smallZoomRect;
smallLayer.contents = (__bridge id)smallImage.CGImage;
I add it to the container view that I am using for the animation. For the animation part, I tried this:
[CATransaction begin];
[CATransaction setAnimationDuration:5.0];
[CATransaction setDisableActions:YES];
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
anim.fromValue = [NSNumber numberWithFloat:1.0];
anim.toValue = [NSNumber numberWithFloat:2.0];
[self.smallZoomLayer addAnimation:anim forKey:@"Zoom"];
My first question is, how do I make it grow towards the center of the view regardless of where the animation starts from. Since the user is clicking on a collection view cell, it currently just grows from the center of that cell versus growing towards the center of the view.
My second question is how to cross fade with the next screen that I am going to show. I thought I could do something like this: How to crossfade between 2 images on iPhone using Core Animation to get the cross fading, but I do not know how to take a screenshot of a view that is not currently being shown yet.
Any thoughts? If this approach is wrong, let me know! Thanks.
Upvotes: 0
Views: 631
Reputation: 104082
Here's another way that I worked on. It doesn't use a snapshot, but instead scales the actual view of the PageViewController, adds it as a subview underneath the coverView, in containerView, and then does a similar animation. I don't know if this is any better from a "good coding practices" standpoint, but it just shows another way to go about it (I've been teaching myself this stuff, so I'm trying out different alternatives).
@interface ViewController ()
@property (strong,nonatomic) UIView *pageView;
@property (strong,nonatomic) PageController *pageVC;
@end
@implementation ViewController
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.pageVC = [self.storyboard instantiateViewControllerWithIdentifier:@"Page"]; // controller with the page contnent
self.pageView = self.pageVC.view;
self.pageView.transform = CGAffineTransformMakeScale(self.coverView.frame.size.width/self.pageView.frame.size.width, self.coverView.frame.size.height/self.pageView.frame.size.height);
[self.containerView insertSubview:self.pageView belowSubview:self.coverView];
[self.containerView constrainViewEqual:self.pageView];
}
-(IBAction)openCover:(id)sender {
self.coverView.layer.anchorPoint = CGPointMake(0, .5);
self.coverView.center = CGPointMake(self.coverView.center.x - self.coverView.frame.size.width/2, self.coverView.center.y);
[self.containerView removeConstraints:self.containerView.constraints];
[self.view removeConstraints:self.view.constraints];
[self.view constrainViewEqual:self.containerView];
[self.containerView constrainViewLeft:self.coverView];
[self.containerView constrainViewEqual:self.pageView];
NSInteger animationTime = 1;
[UIView animateWithDuration:animationTime animations:^{
self.pageView.transform = CGAffineTransformIdentity;
[self.view layoutIfNeeded];
}];
[UIView animateWithDuration:animationTime animations:^{
CATransform3D transform = CATransform3DIdentity;
transform =CATransform3DMakeRotation(M_PI_2, 0.0, -1.0, 0.0);
transform.m34 = 1/1000.0;
transform.m14 = -2/10000.0;
self.coverView.layer.transform =transform;
} completion:^(BOOL finished) {
[self.view.window addSubview:self.pageView];
[self.view.window setRootViewController:self.pageVC];
}];
}
Upvotes: 1
Reputation: 104082
Here is my attempt to recreate the iBooks open cover animation. It looks quite good I think. ViewController is the initial controller that has an image view (imageView) inside a UIView (pageView) of the same size, which is 90 x 122 in my example. imageView has an image of a book cover, and I add a subview underneath imageView of a snapshot of the other controller's view (FirstPageController) -- I had a question about making these snapshots, and I saw you posted one too. I solved it by what seems like a hack -- I added FirstPageController's view as a subview of my view, did the snapshotting, and then removed it from my view. Hopefully, someone will provide a better way to do this. I did some of the sizing and centering using NSLayoutConstraints, and I have a category that I use in other projects to keep that code separated out. Here is the ViewController.m code:
#import "ViewController.h"
#import "UIView+RDConstraintsAdditions.h"
#import <QuartzCore/QuartzCore.h>
#import "FirstPageController.h"
@interface ViewController ()
@property (strong,nonatomic) FirstPageController *fpController;
@end
@implementation ViewController
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.fpController = [self.storyboard instantiateViewControllerWithIdentifier:@"FirstPage"];
[self.view insertSubview:self.fpController.view belowSubview:self.view];
UIGraphicsBeginImageContext(self.fpController.view.bounds.size);
[self.fpController.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.fpController.view removeFromSuperview];
self.imageView2 = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 90, 122)];
self.imageView2.image = viewImage;
[self.pageView insertSubview:self.imageView2 belowSubview:self.imageView];
}
-(IBAction)expandImageView:(id)sender {
self.imageView.layer.anchorPoint = CGPointMake(0, .5);
self.imageView.center = CGPointMake(self.imageView.center.x - self.imageView.frame.size.width/2, self.imageView.center.y);
[self.view removeConstraints:self.view.constraints];
[self.pageView removeConstraints:self.pageView.constraints];
[self.view constrainViewEqual:self.pageView];
[self.pageView constrainViewLeft:self.imageView];
[self.pageView constrainViewEqual:self.imageView2];
[UIView animateWithDuration:2 animations:^{
[self.view layoutIfNeeded];
}];
[UIView animateWithDuration:2 animations:^{
CATransform3D transform = CATransform3DIdentity;
transform =CATransform3DMakeRotation(M_PI_2, 0.0, -1.0, 0.0);
transform.m34 = 1/1000.0;
transform.m14 = -2/10000.0;
self.imageView.layer.transform =transform;
} completion:^(BOOL finished) {
[self.view.window setRootViewController:self.fpController];
}];
}
Here is the category on UIView that I used:
#import "UIView+RDConstraintsAdditions.h"
@implementation UIView (RDConstraintsAdditions)
-(void)constrainViewEqual:(UIView *) view {
[view setTranslatesAutoresizingMaskIntoConstraints:NO];
NSLayoutConstraint *con1 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:0 toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
NSLayoutConstraint *con2 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterY relatedBy:0 toItem:view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
NSLayoutConstraint *con3 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:0 toItem:view attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
NSLayoutConstraint *con4 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:0 toItem:view attribute:NSLayoutAttributeHeight multiplier:1 constant:0];
NSArray *constraints = @[con1,con2,con3,con4];
[self addConstraints:constraints];
}
-(void)constrainViewLeft:(UIView *) view {
[view setTranslatesAutoresizingMaskIntoConstraints:NO];
NSLayoutConstraint *con1 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeLeft relatedBy:0 toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
NSLayoutConstraint *con2 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterY relatedBy:0 toItem:view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
NSLayoutConstraint *con3 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:0 toItem:view attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
NSLayoutConstraint *con4 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:0 toItem:view attribute:NSLayoutAttributeHeight multiplier:1 constant:0];
NSArray *constraints = @[con1,con2,con3,con4];
[self addConstraints:constraints];
}
At the end of the animation, I switch out the root view controller of the window to get the "live" FirstPageController view instead of the snapshot.
Upvotes: 0