Creating a larger flip-side view for 'tile' in storyboard - ios

I currently have a Container View Controller which is home to a button. When this is pressed, I want the view to flip and grow, similar to iTunes on the iPad.
Main View Controller Layout:
Flipping the Container:
When the flip button is pressed, I intend to use this code (Inside the container view's View Controller) to flip the container:
FrontVC *cb = [[FrontVC alloc]initWithFrame:CGRectMake(0, 0, 92, 65)];
FlipVC *cf = [[FlipVC alloc]initWithFrame:CGRectMake(0, 0, 92, 65)];
[UIView transitionWithView:self.view
duration:1
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
// Remove last view
for (UIView *subview in self.view.subviews) {
[subview removeFromSuperview];
}
[self.view addSubview:(displayingPrimary ? cb: cf)];
}
completion:^(BOOL finished) {
if (finished) {
displayingPrimary = !displayingPrimary;
}
}];
This works well if the two views are the same size, and I can lay out the views using code. FrontVC and FlipVC are sub-classes of UIView.
I was thinking about doing something like this to move the view to the centre of the screen, but I'm stuck from there. Hey, I don't even know if this would work! Is there a better way to do this?
if (!displayingPrimary) {
self.view.center = CGPointMake(320.0f, 480.0f);
cb.center = CGPointMake(320.0f, 480.0f);
}
else
{
self.view.center = CGPointMake(59, 324);
}
What I Am Aiming For:
Ideally, I would like to be able to design the flip view in storyboard, and have the 'tile' grow, similar to iTunes:
Whereas in my example I would like it to look like this:
How would I be able to design this in storyboard? If I try to make a view controller, I am unable to resize it to the size that I would like the flip size view controller to be.

The FlipView project is quite polished and looks exactly as the iTunes flip does. I used two imageViews for the demo project. The actual flip code is as follows:
- (IBAction)flip
{
UIView *fromView, *toView;
CGPoint pt;
CGSize size;
CGFloat sense;
if(showingView == a) {
fromView = a;
toView = b;
pt = BORIGIN;
size = BRECT.size;
sense = -1;
} else {
fromView = b;
toView = a;
pt = AORIGIN;
size = ARECT.size;
sense = 1;
}
[UIView animateWithDuration:TIME/2 animations:^
{
CATransform3D t = CATransform3DIdentity;
t.m34 = M34;
t = CATransform3DRotate(t, M_PI_2*sense, 0, 1, 0);
containerView.layer.transform = t;
containerView.frame = CRECT; // midpoint
NSLog(#"Container MID Frame %#", NSStringFromCGRect(containerView.frame));
}
completion:^(BOOL isFinished)
{
toView.layer.transform = CATransform3DMakeRotation(M_PI*sense, 0, 1, 0);
[UIView animateWithDuration:TIME/2 animations:^
{
[fromView removeFromSuperview];
[containerView addSubview:toView];
CATransform3D t = CATransform3DIdentity;
t.m34 = M34;
t = CATransform3DRotate(t, M_PI*sense, 0, 1, 0);
containerView.layer.transform = t; //finish the flip
containerView.frame = (CGRect){ pt, size};
} completion:^(BOOL isFinished)
{
NSLog(#"Container Frame %#", NSStringFromCGRect(containerView.frame));
NSLog(#"A Frame %#", NSStringFromCGRect(b.frame));
NSLog(#"B Frame %#", NSStringFromCGRect(b.frame));
toView.layer.transform = CATransform3DIdentity;
containerView.layer.transform = CATransform3DIdentity;
showingView = showingView == a ? b : a;
}];
}];
}
http://dl.dropbox.com/u/60414145/FlipView.zip
EDIT: One way to work around the resizing a real view with real controls in it might have is to take a snapshot of that view, use the snapshot while zooming/flipping, and in the final completion block switch out the image for the real view. That is:
create a UIImage of your current small view when the user taps the image
create a UIImage of the final view, rendered into a context as its natural size
use the two images in the container view, for the animation
in the final completion block, remove your imageView from view and put the second view now in self.view subviews

I did a rough code (not at all perfect) on this (hope it is what you are looking for). Have a look into it. I created a baseView and two flip views, after the first flip, I increased the frame size of baseView and added the second flip view on it.I am not an expert of Story Board, so I havent used it here.
FlipTest
-anoop

Related

View's position is changed while adding a child view?

I have two views side by side(left & right).At one time i am just showing one view other view not visible because i have moved that view out of scene towards the right of screen.I have set the x position in such a way so that it is not visible(view is always to the right of screen so not visible).So i press next button which is near to first view then my second view is visible & first view is hidden. I have used animation to slide thew view from left to right & right to left.
-(void)handleSingleTapGesture:(UITapGestureRecognizer *)tapGestureRecognizer
{
NSLog(#"first");
CGFloat screenWidth = 0.0;
if(IS_IPHONE_6)
{
screenWidth=self.view.frame.size.width-120.0;
}
if(IS_IPHONE_5)
{
screenWidth=self.view.frame.size.width-68.0;
}
NSLog(#"New screen width is %f",screenWidth);
[self.back_view setHidden:false];
[self.front_view setHidden:true];
[UIView animateWithDuration:0.5 animations:^{
self.front_view.frame = CGRectOffset(self.front_view.frame, -screenWidth, 0.0);
self.back_view.frame = CGRectOffset(self.back_view.frame, -screenWidth, 0.0);
}];
}
-(void)handleSingleTapGesture_one:(UITapGestureRecognizer *)tapGestureRecognizer
{
CGFloat screenWidth = 0.0;
if(IS_IPHONE_6)
{
screenWidth=self.view.frame.size.width-120.0;
}
if(IS_IPHONE_5)
{
screenWidth=self.view.frame.size.width-68.0;
}
NSLog(#"New screen width is %f",screenWidth);
[self.back_view setHidden:true];
[self.front_view setHidden:false];
[UIView animateWithDuration:0.5 animations:^{
self.front_view.frame = CGRectOffset(self.front_view.frame, screenWidth, 0.0);
self.back_view.frame = CGRectOffset(self.back_view.frame, screenWidth, 0.0);
}];
}
On the backView i have added a subview on bottom-right so when ever i try to add the view inside that view then then main view is shifted to the right i don't know why.Please tell me how can i get the solution?

Move a UIView with start point

I have this code below who move my UIView to left:
- (void)viewDidLoad {
[super viewDidLoad];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:4];
int xOriginal = 91;
CGRect rect = imagem.frame;
int x = rect.origin.x;
int y = rect.origin.y;
int w = rect.size.width;
int h = rect.size.height;
if(x == xOriginal){
imagem.frame = CGRectMake(x+100, y, w, h);
}else{
imagem.frame = CGRectMake(x-100, y, w, h);
}
[UIView commitAnimations];
}
My coordinate of my view is x = 91 (Center of the superView), When I start my app my UIView start left and go to center, instead of center and go to right, Why this is happening?
How to make my UIView start in center (x=91) and go to right (91+100), instead of left to center?
[UIImageView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
imagem.frame=CGRectMake(imagem.frame.origin.x+100, imagem.frame.origin.y, imagem.frame.size.width, imagem.frame.size.height);
} completion:^(BOOL finished) {
//code for completion
NSLog(#"Animation complete");
}];
Autolayout and frame animations are not good friends. Try to animate constraints instead of directly the frame.
You can create outlets of your constraints, and set the constant property of the constraint in your code to move your views.
You also call -[view layoutIfNeeded] to refresh your constraints while in an animation block.
Else you can remove all of your view constraints and animate its frame fearlessly.
Initially image.frame = CGRectMake(91,100,20,20);
So imageview starting point is 0
[UIView animateWithDuration:5.0
animations:^{
//Animation code goes here
image.frame = CGRectMake(191,100,20,20); //Now imageview moves from 0 to 100
} completion:^(BOOL finished) {
//Code to run once the animation is completed goes here
}];
As the other poster says, you can't animate a view's frame in XIBs/storyboards that use AutoLayout.
Instead you have to animate a constraint that's attached to the view.
What you do is to create a constraint (or multiple constraints) and connect it to an IBOutlet. Then, in your animation code, you calculate the changes to the constraint(s) and change the constant for the appropriate constraints.
For example, if you wanted to shift the vertical position of a view, set up a constraint that sets the vertical position of your view, and link it to an IBOutlet called viewConstraint. Then you'd use code like this:
[myView animateWithDuration: .25
animations: ^
{
viewConstraint.constant -= shiftAmount;
[self.view layoutIfNeeded];
}
];

Show scaled UIViewController in another UIViewController

I want to display a scaled down "preview" of another ViewController in another ViewController. Is this possible? No interaction are needed so it can basically be a screenshot, but scaled down. I can be solved by manually take a screenshot and save it as an image, but the ViewController contains a lot of localizations and are constantly updated, so if this can be done programatically.
https://www.dropbox.com/s/kd5vinrym1k7afk/Screenshot%202014-05-13%2015.20.17.png
Is this possible?
Edit: the sub view is stored in a storyboard
Edit: This is how I've solved it:
//Load the viewcontroller and view
previewViewController = [self.storyboard instantiateViewControllerWithIdentifier:[PLACEHOLDER_VIEWCONTROLLER_NAMES objectAtIndex:self.identifier]];
[self addChildViewController:previewViewController];
[self.placeholderView.superview addSubview:previewViewController.view];
previewViewController.view.transform = CGAffineTransformMakeScale(scale*2, scale*2);
CGPoint center = self.placeholderView.center;
center.y += 25;
previewViewController.view.center = center;
Use the viewController.view and then scale it down like:
viewController.view.transform = CGAffineTransformMakeScale(0.5, 0.5);
Add your another view controller view as subview to first viewcontroller
[UIView animateWithDuration:2.0f animations:^{
InnerView.userInteractionEnabled = NO;
//Always starts from original scale
InnerView.transform = CGAffineTransformMakeScale(0.5, 0.5);
//Incremental scale
//InnerView.transform = CGAffineTransformScale(InnerView.transform, 0.5, 0.5);
} completion:^(BOOL finished) {
NSLog(#"in Half size");
}];
[UIView animateWithDuration:2.0f animations:^{
InnerView.transform = CGAffineTransformIdentity;
} completion:^(BOOL finished) {
InnerView.userInteractionEnabled = YES;
NSLog(#"Normal size");
}];

UIScrollView inside a movable UIView

I'm attempting to recreate the animation done in the Google Maps app that occurs when you select a location on the map. After selecting a location a UIView pokes up from the bottom of the screen. You can pull that up to reveal a UIScrollView with additional content. I'm curious how they make it so you can only scroll the content inside the UIScrollView when the parent view is at the "top". I understand that you could setScrollEnabled:, but they somehow do it on the fly so that when sliding your finger up the parent view "docks", and then the inner content scrolls, and when you are scrolling the content down, it stops once it reaches the top of the content and starts pulling the header down with it.
Any ideas?
I solved this problem by doing something like this: Create an animation that moves the scroll view's parent from the half-visible position to the top....
- (void)setScrollViewExpanded:(BOOL)expanded {
BOOL isExpanded = self.scrollViewContainer.frame.origin.y == 0.0;
if (isExpanded == expanded) return;
CGRect frame = self.scrollViewContainer.frame;
CGRect newFrame;
if (expanded) {
newFrame = CGRectMake(0, 0, frame.size.width, self.view.frame.size.height);
self.scrollView.delegate = nil;
self.scrollViewContainer.frame = CGRectMake(0, frame.origin.y, frame.size.width, self.view.bounds.size.height);
self.scrollView.delegate = self;
} else {
newFrame = CGRectMake(0, 300, frame.size.width, frame.size.height);
}
[UIView animateWithDuration:1 animations:^{
self.scrollViewContainer.frame = newFrame;
} completion:^(BOOL finished) {
if (!expanded) {
self.scrollView.delegate = nil;
self.scrollViewContainer.frame = CGRectMake(0, 300, self.scrollViewContainer.bounds.size.width, self.view.bounds.size.height-300);
self.scrollView.delegate = self;
}
}];
}
Trigger the animation based on change in scroll view's content offset relative to the top...
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.contentOffset.y > 10.0) {
// 10 is a little threshold so we won't trigger this on a scroll view
// "bounce" at the top. alternatively, you can set scrollView.bounces = NO
[self setScrollViewExpanded:YES];
} else if (scrollView.contentOffset.y < 0.0) {
[self setScrollViewExpanded:NO];
}
}
Edit: I changed the expand animation after rechecking my old code. It needs to be a little more complex due to feedback on the content offset while changing the frame. The edit doesn't change the size during the animation, just the origin. It changes the size before or after the animation and blocks the delegate messages temporarily. Also note the scroll view's autoresize mask should be set to fill the container view.

How do I make an expand/contract transition between views on iOS?

I'm trying to make a transition animation in iOS where a view or view controller appears to expand to fill the whole screen, then contract back to its former position when done. I'm not sure what this type of transition is officially called, but you can see an example in the YouTube app for iPad. When you tap one of the search result thumbnails on the grid, it expands from the thumbnail, then contracts back into the thumbnail when you return to the search.
I'm interested in two aspects of this:
How would you make this effect when transitioning between one view and another? In other words, if view A takes up some area of the screen, how would you transition it to view B which takes up the whole screen, and vice versa?
How would you transition to a modal view this way? In other words, if UIViewController C is currently showing and contains view D which takes up part of the screen, how do you make it look like view D is turning into UIViewController E which is presented modally on top of C?
Edit: I'm adding a bounty to see if that gets this question more love.
Edit: I've got some source code that does this, and Anomie's idea works like a charm, with a few refinements. I had first tried animating the modal controller's view (E), but it didn't produce the effect of feeling like you're zooming into the screen, because it wasn't expanding all the stuff around the thumbnail view in (C). So then I tried animating the original controller's view (C), but the redrawing of it made for a jerky animation, and things like background textures did not zoom properly. So what I wound up doing is taking an image of the the original view controller (C) and zooming that inside the modal view (E). This method is substantially more complex than my original one, but it does look nice! I think it's how iOS must do its internal transitions as well. Anyway, here's the code, which I've written as a category on UIViewController.
UIViewController+Transitions.h:
#import <Foundation/Foundation.h>
#interface UIViewController (Transitions)
// make a transition that looks like a modal view
// is expanding from a subview
- (void)expandView:(UIView *)sourceView
toModalViewController:(UIViewController *)modalViewController;
// make a transition that looks like the current modal view
// is shrinking into a subview
- (void)dismissModalViewControllerToView:(UIView *)view;
#end
UIViewController+Transitions.m:
#import "UIViewController+Transitions.h"
#implementation UIViewController (Transitions)
// capture a screen-sized image of the receiver
- (UIImageView *)imageViewFromScreen {
// make a bitmap copy of the screen
UIGraphicsBeginImageContextWithOptions(
[UIScreen mainScreen].bounds.size, YES,
[UIScreen mainScreen].scale);
// get the root layer
CALayer *layer = self.view.layer;
while(layer.superlayer) {
layer = layer.superlayer;
}
// render it into the bitmap
[layer renderInContext:UIGraphicsGetCurrentContext()];
// get the image
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// close the context
UIGraphicsEndImageContext();
// make a view for the image
UIImageView *imageView =
[[[UIImageView alloc] initWithImage:image]
autorelease];
return(imageView);
}
// make a transform that causes the given subview to fill the screen
// (when applied to an image of the screen)
- (CATransform3D)transformToFillScreenWithSubview:(UIView *)sourceView {
// get the root view
UIView *rootView = sourceView;
while (rootView.superview) rootView = rootView.superview;
// convert the source view's center and size into the coordinate
// system of the root view
CGRect sourceRect = [sourceView convertRect:sourceView.bounds toView:rootView];
CGPoint sourceCenter = CGPointMake(
CGRectGetMidX(sourceRect), CGRectGetMidY(sourceRect));
CGSize sourceSize = sourceRect.size;
// get the size and position we're expanding it to
CGRect screenBounds = [UIScreen mainScreen].bounds;
CGPoint targetCenter = CGPointMake(
CGRectGetMidX(screenBounds),
CGRectGetMidY(screenBounds));
CGSize targetSize = screenBounds.size;
// scale so that the view fills the screen
CATransform3D t = CATransform3DIdentity;
CGFloat sourceAspect = sourceSize.width / sourceSize.height;
CGFloat targetAspect = targetSize.width / targetSize.height;
CGFloat scale = 1.0;
if (sourceAspect > targetAspect)
scale = targetSize.width / sourceSize.width;
else
scale = targetSize.height / sourceSize.height;
t = CATransform3DScale(t, scale, scale, 1.0);
// compensate for the status bar in the screen image
CGFloat statusBarAdjustment =
(([UIApplication sharedApplication].statusBarFrame.size.height / 2.0)
/ scale);
// transform to center the view
t = CATransform3DTranslate(t,
(targetCenter.x - sourceCenter.x),
(targetCenter.y - sourceCenter.y) + statusBarAdjustment,
0.0);
return(t);
}
- (void)expandView:(UIView *)sourceView
toModalViewController:(UIViewController *)modalViewController {
// get an image of the screen
UIImageView *imageView = [self imageViewFromScreen];
// insert it into the modal view's hierarchy
[self presentModalViewController:modalViewController animated:NO];
UIView *rootView = modalViewController.view;
while (rootView.superview) rootView = rootView.superview;
[rootView addSubview:imageView];
// make a transform that makes the source view fill the screen
CATransform3D t = [self transformToFillScreenWithSubview:sourceView];
// animate the transform
[UIView animateWithDuration:0.4
animations:^(void) {
imageView.layer.transform = t;
} completion:^(BOOL finished) {
[imageView removeFromSuperview];
}];
}
- (void)dismissModalViewControllerToView:(UIView *)view {
// take a snapshot of the current screen
UIImageView *imageView = [self imageViewFromScreen];
// insert it into the root view
UIView *rootView = self.view;
while (rootView.superview) rootView = rootView.superview;
[rootView addSubview:imageView];
// make the subview initially fill the screen
imageView.layer.transform = [self transformToFillScreenWithSubview:view];
// remove the modal view
[self dismissModalViewControllerAnimated:NO];
// animate the screen shrinking back to normal
[UIView animateWithDuration:0.4
animations:^(void) {
imageView.layer.transform = CATransform3DIdentity;
}
completion:^(BOOL finished) {
[imageView removeFromSuperview];
}];
}
#end
You might use it something like this in a UIViewController subclass:
#import "UIViewController+Transitions.h"
...
- (void)userDidTapThumbnail {
DetailViewController *detail =
[[DetailViewController alloc]
initWithNibName:nil bundle:nil];
[self expandView:thumbnailView toModalViewController:detail];
[detail release];
}
- (void)dismissModalViewControllerAnimated:(BOOL)animated {
if (([self.modalViewController isKindOfClass:[DetailViewController class]]) &&
(animated)) {
[self dismissModalViewControllerToView:thumbnailView];
}
else {
[super dismissModalViewControllerAnimated:animated];
}
}
Edit: Well, it turns out that doesn't really handle interface orientations other than portrait. So I had to switch to animating the transition in a UIWindow using a view controller to pass along the rotation. See the much more complicated version below:
UIViewController+Transitions.m:
#interface ContainerViewController : UIViewController { }
#end
#implementation ContainerViewController
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)toInterfaceOrientation {
return(YES);
}
#end
...
// get the screen size, compensating for orientation
- (CGSize)screenSize {
// get the size of the screen (swapping dimensions for other orientations)
CGSize size = [UIScreen mainScreen].bounds.size;
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) {
CGFloat width = size.width;
size.width = size.height;
size.height = width;
}
return(size);
}
// capture a screen-sized image of the receiver
- (UIImageView *)imageViewFromScreen {
// get the root layer
CALayer *layer = self.view.layer;
while(layer.superlayer) {
layer = layer.superlayer;
}
// get the size of the bitmap
CGSize size = [self screenSize];
// make a bitmap to copy the screen into
UIGraphicsBeginImageContextWithOptions(
size, YES,
[UIScreen mainScreen].scale);
CGContextRef context = UIGraphicsGetCurrentContext();
// compensate for orientation
if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
CGContextTranslateCTM(context, size.width, 0);
CGContextRotateCTM(context, M_PI_2);
}
else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
CGContextTranslateCTM(context, 0, size.height);
CGContextRotateCTM(context, - M_PI_2);
}
else if (self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
CGContextTranslateCTM(context, size.width, size.height);
CGContextRotateCTM(context, M_PI);
}
// render the layer into the bitmap
[layer renderInContext:context];
// get the image
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// close the context
UIGraphicsEndImageContext();
// make a view for the image
UIImageView *imageView =
[[[UIImageView alloc] initWithImage:image]
autorelease];
// done
return(imageView);
}
// make a transform that causes the given subview to fill the screen
// (when applied to an image of the screen)
- (CATransform3D)transformToFillScreenWithSubview:(UIView *)sourceView
includeStatusBar:(BOOL)includeStatusBar {
// get the root view
UIView *rootView = sourceView;
while (rootView.superview) rootView = rootView.superview;
// by default, zoom from the view's bounds
CGRect sourceRect = sourceView.bounds;
// convert the source view's center and size into the coordinate
// system of the root view
sourceRect = [sourceView convertRect:sourceRect toView:rootView];
CGPoint sourceCenter = CGPointMake(
CGRectGetMidX(sourceRect), CGRectGetMidY(sourceRect));
CGSize sourceSize = sourceRect.size;
// get the size and position we're expanding it to
CGSize targetSize = [self screenSize];
CGPoint targetCenter = CGPointMake(
targetSize.width / 2.0,
targetSize.height / 2.0);
// scale so that the view fills the screen
CATransform3D t = CATransform3DIdentity;
CGFloat sourceAspect = sourceSize.width / sourceSize.height;
CGFloat targetAspect = targetSize.width / targetSize.height;
CGFloat scale = 1.0;
if (sourceAspect > targetAspect)
scale = targetSize.width / sourceSize.width;
else
scale = targetSize.height / sourceSize.height;
t = CATransform3DScale(t, scale, scale, 1.0);
// compensate for the status bar in the screen image
CGFloat statusBarAdjustment = includeStatusBar ?
(([UIApplication sharedApplication].statusBarFrame.size.height / 2.0)
/ scale) : 0.0;
// transform to center the view
t = CATransform3DTranslate(t,
(targetCenter.x - sourceCenter.x),
(targetCenter.y - sourceCenter.y) + statusBarAdjustment,
0.0);
return(t);
}
- (void)expandView:(UIView *)sourceView
toModalViewController:(UIViewController *)modalViewController {
// get an image of the screen
UIImageView *imageView = [self imageViewFromScreen];
// show the modal view
[self presentModalViewController:modalViewController animated:NO];
// make a window to display the transition on top of everything else
UIWindow *window =
[[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
window.hidden = NO;
window.backgroundColor = [UIColor blackColor];
// make a view controller to display the image in
ContainerViewController *vc = [[ContainerViewController alloc] init];
vc.wantsFullScreenLayout = YES;
// show the window
[window setRootViewController:vc];
[window makeKeyAndVisible];
// add the image to the window
[vc.view addSubview:imageView];
// make a transform that makes the source view fill the screen
CATransform3D t = [self
transformToFillScreenWithSubview:sourceView
includeStatusBar:(! modalViewController.wantsFullScreenLayout)];
// animate the transform
[UIView animateWithDuration:0.4
animations:^(void) {
imageView.layer.transform = t;
} completion:^(BOOL finished) {
// we're going to crossfade, so change the background to clear
window.backgroundColor = [UIColor clearColor];
// do a little crossfade
[UIView animateWithDuration:0.25
animations:^(void) {
imageView.alpha = 0.0;
}
completion:^(BOOL finished) {
window.hidden = YES;
[window release];
[vc release];
}];
}];
}
- (void)dismissModalViewControllerToView:(UIView *)view {
// temporarily remove the modal dialog so we can get an accurate screenshot
// with orientation applied
UIViewController *modalViewController = [self.modalViewController retain];
[self dismissModalViewControllerAnimated:NO];
// capture the screen
UIImageView *imageView = [self imageViewFromScreen];
// put the modal view controller back
[self presentModalViewController:modalViewController animated:NO];
[modalViewController release];
// make a window to display the transition on top of everything else
UIWindow *window =
[[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
window.hidden = NO;
window.backgroundColor = [UIColor clearColor];
// make a view controller to display the image in
ContainerViewController *vc = [[ContainerViewController alloc] init];
vc.wantsFullScreenLayout = YES;
// show the window
[window setRootViewController:vc];
[window makeKeyAndVisible];
// add the image to the window
[vc.view addSubview:imageView];
// make the subview initially fill the screen
imageView.layer.transform = [self
transformToFillScreenWithSubview:view
includeStatusBar:(! self.modalViewController.wantsFullScreenLayout)];
// animate a little crossfade
imageView.alpha = 0.0;
[UIView animateWithDuration:0.15
animations:^(void) {
imageView.alpha = 1.0;
}
completion:^(BOOL finished) {
// remove the modal view
[self dismissModalViewControllerAnimated:NO];
// set the background so the real screen won't show through
window.backgroundColor = [UIColor blackColor];
// animate the screen shrinking back to normal
[UIView animateWithDuration:0.4
animations:^(void) {
imageView.layer.transform = CATransform3DIdentity;
}
completion:^(BOOL finished) {
// hide the transition stuff
window.hidden = YES;
[window release];
[vc release];
}];
}];
}
Whew! But now it looks just about like Apple's version without using any restricted APIs. Also, it works even if the orientation changes while the modal view is in front.
Making the effect is simple. You take the full-sized view, initialize its transform and center to position it on top of the thumbnail, add it to the appropriate superview, and then in an animation block reset the transform and center to position it in the final position. To dismiss the view, just do the opposite: in an animation block set transform and center to position it on top of the thumbnail, and then remove it completely in the completion block.
Note that trying to zoom from a point (i.e. a rectangle with 0 width and 0 height) will screw things up. If you're wanting to do that, zoom from a rectangle with width/height something like 0.00001 instead.
One way would be to do the same as in #1, and then call presentModalViewController:animated: with animated NO to present the actual view controller when the animation is complete (which, if done right, would result in no visible difference due to the presentModalViewController:animated: call). And dismissModalViewControllerAnimated: with NO followed by the same as in #1 to dismiss.
Or you could manipulate the modal view controller's view directly as in #1, and accept that parentViewController, interfaceOrientation, and some other stuff just won't work right in the modal view controller since Apple doesn't support us creating our own container view controllers.
After watching the Youtube iPad animation, I figured out that it's just an illusion. Let's say that there's a SearchViewController for the search results, and a DetailViewController for the video itself, and the additional info of the video.
DetailViewController has a method like - (id)initWithFullscreen which starts the view controller using the full screen space with the video.
So the sequence goes like this:
SearchViewController presents its results.
User clicks on a video.
DetailViewController is created with initWithFullscreen, but not presented
The "Zoom in" animation begins. (Notice that we are still on the SearchViewController, and this animation is just a simple View animation)
The "Zoom in" animation ends, presents the DetailViewController with animated:NO (as Anomie mentioned).
The DetailViewController is now presented, and using full space.
It doesn't seem that the youtube app is doing anything fancier, the give-away was that the "Zoom in" animation zooms to a black square, before presenting the full video.

Resources