I'm trying to do an effect to open and close a view(menu) the view has origin x = 55.
the open effect works properly good, but the close effect has an problem:
the effect begins at x = 0 and my view is at x = 55...then the effect appears strange....
There's my code:
-(IBAction)menuClick:(id)sender {
if(!self.viewMais) {
CGRect screen = [[UIScreen mainScreen]bounds];
self.viewMais = [[UIView alloc]initWithFrame:CGRectMake(55, 0, screen.size.width-55, screen.size.height)];
[self.viewMais setBackgroundColor:[UIColor blackColor]];
[self.view addSubview:self.viewMais];
[self open];
}
else {
[self close];
}
}
-(void)open {
CATransition *transition = [CATransition animation];
transition.duration = 0.3;
transition.timingFunction =
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionMoveIn;
transition.subtype = kCATransitionFromRight;
UIView *containerView = self.viewMais;
[containerView.layer addAnimation:transition forKey:nil];
}
-(void)close {
CATransition *transition = [CATransition animation];
transition.duration = 0.3;
transition.timingFunction =
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionMoveIn;
transition.subtype = kCATransitionFromLeft;
UIView *containerView = self.viewMais;
[containerView.layer addAnimation:transition forKey:nil];
[self performSelector:#selector(removerViewMais) withObject:nil afterDelay:0.3];
}
-(void) removerViewMais {
[self.viewMais removeFromSuperview];
self.viewMais = nil;
}
-(IBAction)showPicker{
if (!isPickerShow) {
[self.view endEditing:YES];
[UIView animateWithDuration:0.3 animations:^{
viewWithPicker.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height-viewWithPicker.frame.size.height, 320, 555);
isPickerShow=YES;
}];
}
}
-(IBAction)hidePicker{
if (isPickerShow) {
[UIView animateWithDuration:0.3 animations:^{
viewWithPicker.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height , 320, 255);
isPickerShow=NO;
}];
}
}
I solved my problem with the following code in close menu
-(void)close {
CGRect destination = self.view.frame;
[UIView animateWithDuration:0.25 animations:^{
self.viewMais.frame = CGRectMake(destination.size.width, self.viewMais.frame.origin.y, self.viewMais.frame.size.width, self.viewMais.frame.size.height);
} completion:^(BOOL finished) {
[self.viewMais setHidden:YES];
[self.viewMais removeFromSuperview];
self.viewMais = nil;
}];
}
Related
UICollectionView cell flip animation on didSelect method should flip with Detailed information on the of the clicked index cell and if user want to go original view should be able to see on cell.
I am using
UICollectionViewCell* cell = [collectionView cellForItemAtIndexPath:indexPath];
[UIView animateWithDuration:1.0
delay:0
options:(UIViewAnimationOptionAllowUserInteraction)
animations:^
{
[UIView transitionFromView:cell.contentView
toView:cell.contentView
duration:.5
options:UIViewAnimationOptionTransitionFlipFromRight
completion:nil];
}
completion:^(BOOL finished)
{
}
];
In your code the animation removes your from view and show the to view.So, in the collection if the content view is removed then how it come back.
So, don't remove the view, just hide it.
Please use the below code
CVCell* cell1 = (CVCell *)[collectionView cellForItemAtIndexPath:indexPath];
[UIView transitionWithView:cell1.contentView
duration:5
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
if (cell1.isred) {
cell1.isred = NO;
cell1.greenview.hidden = NO;
cell1.redView.hidden=YES;
} else {
cell1.isred = YES;
cell1.greenview.hidden = YES;
cell1.redView.hidden=NO;
}
} completion:nil];
greenview is first view and redview is second which are added on the cell.
Now when fliped first greenview is shown and redview is hidden and next time viceversa
You can try other animation
CATransition *animation = [CATransition animation];
animation.delegate = self;
animation.duration = 2.6f;
animation.timingFunction = UIViewAnimationCurveEaseInOut;
animation.fillMode = kCAFillModeForwards;
animation.startProgress = 0.1;
animation.endProgress = 1.0;
animation.removedOnCompletion = YES;
animation.type = #"cube";//---
animation.subtype = (cell1.isred)?kCATransitionFromLeft:kCATransitionFromRight;
[CATransaction setCompletionBlock:^{
if (cell1.isred) {
cell1.isred = NO;
cell1.greenview.hidden = NO;
cell1.redView.hidden=YES;
} else {
cell1.isred = YES;
cell1.greenview.hidden = YES;
cell1.redView.hidden=NO;
}
}];
[cell1.contentView.layer addAnimation:animation forKey:#"Cameraanimation"];
I have couple of images i want to switch. There is a simple function i made for do this:
-(void)imageSlide{
if (!isMoved){
CGRect frame = self.imageSlideshow.frame;
frame.origin.x = self.imageSlideshow.frame.origin.x - 320;
self.imageSlideshow.frame = frame;
NSLog(#"1 caze work");
isMoved = YES;
}
if (isMoved){
CGRect frame = self.imageSlideshow.frame;
frame.origin.x = self.imageSlideshow.frame.origin.x + 320;
self.imageSlideshow.frame = frame;
NSLog(#"2 caze work");
isMoved = NO;
}
}
There is NSTimer which call that function:
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(imageSlide)
userInfo:nil
repeats:YES];
BOOL isMoved; placed in implementation of class.
What i want is, to remove an image and then, shown again (and repeat it every 2 seconds). It would be nice to have smooth animation as well.
That code:
for (int i=0; i<99; i++){
self.imageSlideshow.image = [UIImage imageNamed:(i % 2) ? #"ipd1.jpg" : #"ipd2.jpg"];
CATransition *transition = [CATransition animation];
transition.duration = 1.0f;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionFade;
[self.imageSlideshow.layer addAnimation:transition forKey:nil];
}
Also not working, no idea why. Image stand still. I did import quartz library and include headers.
you can acheive this by using this code :-
#import <QuartzCore/QuartzCore.h>
...
imageView.image = [UIImage imageNamed:(i % 2) ? #"3.jpg" : #"4.jpg"];
CATransition *transition = [CATransition animation];
transition.duration = 2.0f;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionFade;
[imageView.layer addAnimation:transition forKey:nil];
and NSTimer Job should be done here by transition.duration. So, no need of NSTimer anymore here.
Courtesy :- https://stackoverflow.com/a/2834693/1865424
This piece of code works too :-
// create the view that will execute our animation
UIImageView* imageSlideshow = [[UIImageView alloc] initWithFrame:self.view.frame];
// load all the frames of our animation
imageSlideshow.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#“ipd1.jpg"],
[UIImage imageNamed:#“ipd2.jpg"], nil];
// all frames will execute in 1.75 seconds
imageSlideshow.animationDuration = 1.75;
// repeat the animation forever
imageSlideshow.animationRepeatCount = 0;
// start animating
[imageSlideshow startAnimating];
// add the animation view to the main window
[self.view addSubview:imageSlideshow];
I think you need:
[UIView animateWithDuration:1 options:UIViewAnimationOptionCurveEaseIn
animations:^{
//Change frame here.
} completion:^ (BOOL completed) {}
];
Try this code. It is worked for me.
#pragma mark -
#pragma mark viewDidAppear
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
updateBCK = [NSTimer scheduledTimerWithTimeInterval:(4.0) target:self selector:#selector(changeImage) userInfo:nil repeats:YES];
[updateBCK fire];
}
#pragma mark -
#pragma mark viewDidDisAppear
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
if ([updateBCK isValid]){
[updateBCK invalidate];
updateBCK = nil;
}
}
-(void)changeImage{
static int i=0;
if (i == [myImages count]){
i=0;
}
[UIImageView beginAnimations:nil context:NULL];
[UIImageView setAnimationDuration:0.6];
backgroundImageView.alpha=1;
backgroundImageView.image =[myImages objectAtIndex:i];
[UIImageView commitAnimations];
i++;
}
It is bug in your code. After setting isMoved = YES, you can't check if isMoved == YES.
I don't know if that is what you mean, but try this code:
-(void)imageSlide{
[UIView animateWithDuration:1 animations:^{
if (!isMoved){
CGRect frame = self.imageSlideshow.frame;
frame.origin.x = self.imageSlideshow.frame.origin.x - 320;
self.imageSlideshow.frame = frame;
NSLog(#"1 caze work");
isMoved = YES;
}
else
{
CGRect frame = self.imageSlideshow.frame;
frame.origin.x = self.imageSlideshow.frame.origin.x + 320;
self.imageSlideshow.frame = frame;
NSLog(#"2 caze work");
isMoved = NO;
}
} completion:^(BOOL finished) {
}];
}
EDIT
Maybe this code will help you:
-(void)slideshow
{
static int i = 0;
UIImage * img = [UIImage imageNamed:(i % 2) ? #"ipd1.jpg" : #"ipd2.jpg"];
[UIView transitionWithView:self.imageSlideshow duration:1.0 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
self.imageSlideshow.image = img;
} completion:^(BOOL finished) {
i++;
[self performSelector:#selector(slideshow) withObject:nil afterDelay:2.0];
}];
}
Good day! I have a question,I have this code for a Single Page application
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint endPosition = [touch locationInView:self.view];
if (self.imageStatus>0)
{
if (self.startPosition.x < endPosition.x)
{
// Right swipe
CATransition *transition = [CATransition animation];
transition.duration = 0.75;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
transition.type = kCATransitionPush;
transition.subtype =kCATransitionFromLeft;
transition.delegate = self;
[self.view.layer addAnimation:transition forKey:nil];
[self.view addSubview:myViewController.view];
self.imageStatus --;
if (self.imageStatus == 0)
{
self.myImage.image = [UIImage imageNamed:#"blue_back.png"];
}
else
{
self.myImage.image = [UIImage imageNamed:#"black_back.png"];
}
}
}
if (self.imageStatus<2)
{
if (self.startPosition.x > endPosition.x)
{
// Left swipe
CATransition *transition = [CATransition animation];
transition.duration = 0.75;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
transition.type = kCATransitionPush;
transition.subtype =kCATransitionFromRight;
transition.delegate = self;
[self.view.layer addAnimation:transition forKey:nil];
[self.view addSubview:myViewController.view];
self.imageStatus ++;
if (self.imageStatus == 2)
{
self.myImage.image = [UIImage imageNamed:#"red_back.png"];
}
else
{
self.myImage.image = [UIImage imageNamed:#"black_back.png"];
}
}
}
when i open the app the default background is black, when i swipe at right it shows the blue_back.png or the blue background, when i swipe back it shows the default background which is black, and when its on black again and i will swipe it left it shows the red_back.png or the red background and vise versa. now what i want is, i will add another page with yellow background next to red, so when im in the default black page, when i swipe to left two times it shows the yellow. and also the swiping function, when i swipe it it takes maybe a second to switch a page, how can i implement the "Realtime Swiping" like in the table view, but horizontal, so it changes page fast. thanks!
Updated answer:
What you want is a paging scrollview. The trick is to add the views are the correct positions in the scrollView and set the scrollviews contentSize correctly. Example with two views:
- (void)viewDidLoad
{
[super viewDidLoad];
self.edgesForExtendedLayout = UIRectEdgeBottom;
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
scrollView.pagingEnabled = YES;
scrollView.contentSize = CGSizeMake(self.view.frame.size.width * 2, self.view.bounds.size.height);
[self.view addSubview:scrollView];
UIView *redView = [[UIView alloc] init];
redView.frame = self.view.bounds;
redView.backgroundColor = [UIColor redColor];
[scrollView addSubview:redView];
UIView *blueView = [[UIView alloc] init];
CGRect frame = self.view.bounds;
frame.origin.x = 320;
blueView.frame = frame;
blueView.backgroundColor = [UIColor blueColor];
[scrollView addSubview:blueView];
}
Old answer:
I would be easier to make use of the build in gesture recognizers. Specifically UIPanGestureRecognizer.
Example:
In viewDidLoad:
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(myAction:)];
[self.view addGestureRecognizer:pan];
And the action:
- (void) myAction:(UIPanGestureRecognizer*)gestureRec
{
if (gestureRec.state == UIGestureRecognizerStateEnded) {
NSLog(#"ended");
NSLog(#"%#", NSStringFromCGPoint([gestureRec velocityInView:self.view]));
}
}
The state determines when its ended as you want. And you can use the velocityInView to determine right/left and up/down movement
I'm trying to add a simple push animation between UIViewControllers using a custom segue.
(Without converting my controllers to use a UINavigationController)
The answers found so far work fine with a navigation controller, but fail when not using a navigation controller. (I am still reading and trying other answers I've seen here on stack-overflow)
My custom segue .m thanks to (ZHCustomSegue.m) Zakir on 7/5/12
#import "SFCustomSegue.h"
#import "QuartzCore/QuartzCore.h"
#implementation SFCustomSegue
-(void)perform {
UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
UIViewController *destinationController = (UIViewController*)[self destinationViewController];
CATransition* transition = [CATransition animation];
transition.duration = 4.0;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
[sourceViewController.view.layer addAnimation:transition forKey:kCATransition];
[sourceViewController presentViewController:[self destinationViewController] animated:NO completion:nil];
}
If I use a navigation controller as per Zakir's original example and replace the last 2 lines with:
[sourceViewController.navigationController.view.layer addAnimation:transition
forKey:kCATransition];
[sourceViewController.navigationController pushViewController:destinationController animated:NO];
It works....
Apple's doc says (notice not a navigation controller, and using a modal segue):
- (void)perform
{
// Add your own animation code here.
[[self sourceViewController] presentModalViewController:[self destinationViewController] animated:NO];
}
By NOT Working, I get no animation. In Zakir's code, using 4.0 seconds it takes 4.0 seconds and I get the animation I want. Of course, if I use a navigation controller a push tranition is the default. In this code, I get no animation and it takes 0 seconds.
Has anyone gotten custom segue animations to work with UIViewControllers?
Since you are calling presentViewController, the source view controller is sticking around. If you actually want to replace the source view controller with the destination view controller, so that the source gets dealloced, you can do this:
static UIImageView *screenShotOfView(UIView *view)
{
// Create a snapshot for animation
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
[view.layer renderInContext:context];
UIImageView *screenShot = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
return screenShot;
}
- (void)perform
{
UIViewController *source = (UIViewController *) self.sourceViewController;
UIViewController *destination = (UIViewController *) self.destinationViewController;
// Swap the snapshot out for the source view controller
UIWindow *window = source.view.window;
UIImageView *screenShot = screenShotOfView(source.view);
CGRect originalFrame = destination.view.frame;
BOOL animsEnabled = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
{
window.rootViewController = destination;
[window addSubview:screenShot];
[source.view removeFromSuperview];
CGRect frame = destination.view.frame;
frame.origin.x += source.view.bounds.size.width;
destination.view.frame = frame;
}
[UIView setAnimationsEnabled:animsEnabled];
[UIView animateWithDuration:kAnimationDuration
animations:^{
destination.view.frame = originalFrame;
CGRect frame = screenShot.frame;
frame.origin.x -= screenShot.bounds.size.width;
screenShot.frame = frame;
}
completion:^(BOOL finished) {
[screenShot removeFromSuperview];
}];
}
It appears that my above code, is "nearly" working, but not quite.
The main issue at hand is likely when the final step is called.
My working code looks like this:
#import "SFCustomSegue.h"
#import "QuartzCore/QuartzCore.h"
#implementation SFCustomSegue
static const float delay = 0.5f;
-(void)perform {
UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
UIViewController *destinationController = (UIViewController*)[self destinationViewController];
[sourceViewController.view addSubview:destinationController.view];
CATransition* transition = [CATransition animation];
transition.duration = delay;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
[sourceViewController.view.layer addAnimation:transition forKey:kCATransition];
[self performSelector:#selector(performDone:) withObject:destinationController afterDelay:delay];
}
- (void)performDone:(id)viewController{
UIViewController *destination = (UIViewController*)viewController;
[destination.view removeFromSuperview];
[[self sourceViewController] presentViewController:destination animated:NO completion:nil];
}
#end
The key to resolving this was adding the view for the transition (line 13), then removing it (line 28) and adding it back (line 29) using presentViewController:animated:completion:.
I am still "stuck" with using only the regular CATransition transition types, but that's good enough for me for now, since I want to use kCATransitionPush.
I have merged the ideas from both answers to get this code (working good):
(the answers from above don't count in the orientation of device so they won't work in landscape, for example)
(the variant with CATransitions wasn't appropriate because there was a gap between two views while the one pushed the another)
#import "PushSegue.h"
/*
#interface TransitionDelegate : NSObject
- (id) initWithScreenshot: (UIView*) screenshot;
#end
*/
#implementation PushSegue
- (void) perform
{
UIViewController* source = (UIViewController*) self.sourceViewController;
UIViewController* destination = (UIViewController*) self.destinationViewController;
const BOOL iPad = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
float animationDuration = iPad ? 0.5 : 0.3;
// Swap the snapshot out for the source view controller
UIWindow* window = source.view.window;
UIImageView* screenShot = screenShotOfView(source.view);
// accord to device orientation
float rotation;
//NSString* transitionType;
CGRect originalFrame = destination.view.frame;
CGRect destinationFrame = destination.view.frame;
switch ([UIApplication sharedApplication].statusBarOrientation)
{
case UIInterfaceOrientationPortraitUpsideDown:
rotation = M_PI;
destinationFrame.origin.x -= source.view.bounds.size.width;
//transitionType = kCATransitionFromLeft;
break;
case UIInterfaceOrientationLandscapeLeft:
rotation = M_PI + M_PI_2;
destinationFrame.origin.y -= source.view.bounds.size.width;
//transitionType = kCATransitionFromBottom;
break;
case UIInterfaceOrientationLandscapeRight:
rotation = M_PI_2;
destinationFrame.origin.y += source.view.bounds.size.width;
//transitionType = kCATransitionFromTop;
break;
default:
rotation = 0;
destinationFrame.origin.x += source.view.bounds.size.width;
//transitionType = kCATransitionFromRight;
break;
}
screenShot.transform = CGAffineTransformMakeRotation(rotation);
// reposition after rotation
CGRect screenshotFrame = screenShot.frame;
screenshotFrame.origin.x = 0;
screenshotFrame.origin.y = 0;
screenShot.frame = screenshotFrame;
switch ([UIApplication sharedApplication].statusBarOrientation)
{
case UIInterfaceOrientationPortraitUpsideDown:
screenshotFrame.origin.x += screenShot.bounds.size.width;
break;
case UIInterfaceOrientationLandscapeLeft:
screenshotFrame.origin.y += screenShot.bounds.size.width;
break;
case UIInterfaceOrientationLandscapeRight:
screenshotFrame.origin.y -= screenShot.bounds.size.width;
break;
default:
screenshotFrame.origin.x -= screenShot.bounds.size.width;
break;
}
// swap the view with its screenshot
window.rootViewController = destination;
[window addSubview:screenShot];
[source.view removeFromSuperview];
/*
CATransition* transition = [CATransition animation];
transition.duration = animationDuration;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = transitionType;
transition.delegate = [[TransitionDelegate alloc] initWithScreenshot:screenShot];
[window addSubview:destination.view];
[screenShot.window.layer addAnimation:transition forKey:nil];
*/
const BOOL animationsEnabled = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
{
destination.view.frame = destinationFrame;
}
[UIView setAnimationsEnabled:animationsEnabled];
[UIView animateWithDuration:animationDuration
animations:^
{
destination.view.frame = originalFrame;
screenShot.frame = screenshotFrame;
}
completion:^(BOOL finished)
{
[screenShot removeFromSuperview];
}];
/*
const BOOL animationsEnabled = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
{
CGRect frame = destination.view.frame;
frame.origin.x += source.view.bounds.size.width;
destination.view.frame = frame;
}
[UIView setAnimationsEnabled:animationsEnabled];
[UIView animateWithDuration:animationDuration
animations:^
{
destination.view.frame = originalFrame;
CGRect frame = screenShot.frame;
frame.origin.x -= screenShot.bounds.size.width;
screenShot.frame = frame;
}
completion:^(BOOL finished)
{
[screenShot removeFromSuperview];
}];
*/
/*
CATransition* transition = [CATransition animation];
transition.duration = animationDuration;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
switch ([UIApplication sharedApplication].statusBarOrientation)
{
case UIInterfaceOrientationPortraitUpsideDown:
transition.subtype = kCATransitionFromLeft;
break;
case UIInterfaceOrientationLandscapeLeft:
transition.subtype = kCATransitionFromBottom;
break;
case UIInterfaceOrientationLandscapeRight:
transition.subtype = kCATransitionFromTop;
break;
default:
transition.subtype = kCATransitionFromRight;
break;
}
[source.view.window.layer addAnimation:transition forKey:nil];
[source presentViewController:destination animated:NO completion:nil];
*/
}
static UIImageView* screenShotOfView(UIView* view)
{
// Create a snapshot for animation
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
[view.layer renderInContext:context];
UIImageView* screenShot = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
return screenShot;
}
#end
/*
#implementation TransitionDelegate
{
UIView* screenshot;
}
- (id) initWithScreenshot: (UIView*) screenshot
{
if (self = [super init])
{
self->screenshot = screenshot;
}
return self;
}
- (void) animationDidStop: (CAAnimation*) theAnimation
finished: (BOOL) flag
{
[screenshot removeFromSuperview];
}
#end
*/
I am using this method:
[UIView transitionWithView: duration: options: animations: completion:];
to control a back animation on a segue. Precisely using this code:
[UIView transitionWithView:self.view.superview
duration:2.0
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
[self dismissModalViewControllerAnimated:NO];
}
completion:nil];
That works, but what I really want is not a Flip, it is a Push. In other words I want to see the views sliding from right to left, one replacing the other.
Unfortunately there is no UIViewAnimationOptionTransitionPushFromRight.
Therefore my question: how can I get the effect I want?
If you are using a navigationController, you can do something like this:
- (void)perform
{
UIViewController *source = (UIViewController*)[self sourceViewController];
UIViewController *destination = (UIViewController*)[self destinationViewController];
CATransition* transition = [CATransition animation];
transition.duration = .25;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;
[source.navigationController.view.layer addAnimation:transition
forKey:kCATransition];
[source.navigationController pushViewController:destination animated:NO];
}
If you don't want to use a UINavigationController, and you want the source view controller to go away as opposed to using presentViewController, and you don't want the fade out animation caused by using kCATransitionPush, then the following solution will work. This also works if your views are transparent and you do not want the containing background to animate.
static UIImageView *screenShotOfView(UIView *view)
{
// Create a snapshot for animation
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
[view.layer renderInContext:context];
UIImageView *screenShot = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
return screenShot;
}
- (void)perform
{
UIViewController *source = (UIViewController *) self.sourceViewController;
UIViewController *destination = (UIViewController *) self.destinationViewController;
// Swap the snapshot out for the source view controller
UIWindow *window = source.view.window;
UIImageView *screenShot = screenShotOfView(source.view);
CGRect originalFrame = destination.view.frame;
BOOL animsEnabled = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
{
window.rootViewController = destination;
[window addSubview:screenShot];
[source.view removeFromSuperview];
CGRect frame = destination.view.frame;
frame.origin.x += source.view.bounds.size.width;
destination.view.frame = frame;
}
[UIView setAnimationsEnabled:animsEnabled];
[UIView animateWithDuration:kAnimationDuration
animations:^{
destination.view.frame = originalFrame;
CGRect frame = screenShot.frame;
frame.origin.x -= screenShot.bounds.size.width;
screenShot.frame = frame;
}
completion:^(BOOL finished) {
[screenShot removeFromSuperview];
}];
}