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
*/
Related
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;
}];
}
I created a custom zoom-in transition when pushing a new viewController, and it used to work perfectly fine. Now I want to create a zoom-out effect when popping the viewController, and even-thought the final state is correct, the animation is wrong since I don't know how to identify if it is pushing or popping and all the methods like isBeingPresented return false and presentedViewController is always nil
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
self.transitionContext = transitionContext;
UIView *containerView = [transitionContext containerView];
UIViewController *fromViewController = (UIViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = (UIViewController *) [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
[containerView addSubview:toViewController.view];
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
scaleAnimation.duration = [self transitionDuration:transitionContext];
scaleAnimation.delegate = self;
scaleAnimation.removedOnCompletion = YES;
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
opacityAnimation.duration = [self transitionDuration:transitionContext];
opacityAnimation.delegate = self;
opacityAnimation.removedOnCompletion = YES;
if (toViewController.isBeingPresented) {
scaleAnimation.fromValue = [NSNumber numberWithDouble:0];
scaleAnimation.toValue = [NSNumber numberWithDouble:1];
opacityAnimation.fromValue = [NSNumber numberWithFloat:1];
opacityAnimation.toValue = [NSNumber numberWithFloat:0];
} else {
scaleAnimation.fromValue = [NSNumber numberWithDouble:1];
scaleAnimation.toValue = [NSNumber numberWithDouble:0];
opacityAnimation.fromValue = [NSNumber numberWithFloat:0];
opacityAnimation.toValue = [NSNumber numberWithFloat:1];
}
[toViewController.view.layer addAnimation:scaleAnimation forKey:nil];
[fromViewController.view.layer addAnimation:opacityAnimation forKey:nil];
}
You can save a UINavigationControllerOperation variable:
-(id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController*)fromVC
toViewController:(UIViewController*)toVC {
self.navigationOperation = operation;
return self;
}
then you check it is push or pop:
if (self.navigationOperation == UINavigationControllerOperationPush) {
// push
} else if (self.navigationOperation == UINavigationControllerOperationPop) {
// pop
}
This is not associated with your code above, but this will override the default animation of navigationController..
//.h
#interface YJKit_Navigation : UINavigationController
+ (void)navigation:(UINavigationController *)navigationController withViewController:(UIViewController *)viewController push:(BOOL)isPush;
#end
//.m
#implementation YJKit_Navigation
+ (void)navigation:(UINavigationController *)navigationController withViewController:(UIViewController *)viewController push:(BOOL)isPush
{
[navigationController.view.layer addAnimation: isPush ? PushAnimation : PopAnimation forKey:nil];
isPush ? [navigationController pushViewController:viewController animated:NO] : [navigationController popViewControllerAnimated:NO];
}
#end
and using it like:
[YJKit_Navigation navigation:YourUINavigationController withViewController:YourTargetUIViewController push:(BOOL)];
Hope this will also help you.. :)
See my variant of push viewController with pop animation
- (void)navigationController:(UINavigationController *)navigationController
present:(UIViewController *)viewController
animated:(BOOL)animated
completion:(void (^)(BOOL finished))completion {
self.completion = completion;
if (animated) {
CATransition *animation = [CATransition animation];
[animation setDelegate:self];
[animation setType:kCATransitionMoveIn];
[animation setSubtype:kCATransitionFromTop];
[animation setDuration:kPresentAnimationDuration];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[navigationController.view.layer addAnimation:animation forKey:kCATransition];
}
[navigationController pushViewController:viewController animated:NO];
if (!animated) {
self.completion(YES);
}
}
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];
}];
}
I have a UIViewController that i edit using IB. I put a UINavigationBar and a UISegmentedControl on the top and 3 UIViews under them. I want to be able to switch between the UIViews using an animation, but i only want to animate the UIViews, i want the navigationBar and athe segmentedControl to not move. I show the code how i do it now.
Any idea how i could only move the 3 views?
- (IBAction)segmentedControlValueChanged:(id)sender {
UISegmentedControl* segmentedControl = sender;
if(lastSelectedViewIndex != [segmentedControl selectedSegmentIndex]) {
CATransition *transition = [CATransition animation];
transition.duration = 0.4;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionMoveIn;
if(lastSelectedViewIndex < [segmentedControl selectedSegmentIndex])
transition.subtype = kCATransitionFromLeft;
else
transition.subtype = kCATransitionFromRight;
transition.removedOnCompletion = YES; // force removal of animation when completed.
{
switch ([segmentedControl selectedSegmentIndex]) {
case 0:
[self.usageScenarioView setHidden:NO];
[self.loginCredentialsView setHidden:YES];
[self.whatItCoversView setHidden:YES];
[self.pageControl setCurrentPage:0];
break;
case 1:
[self.usageScenarioView setHidden:YES];
[self.loginCredentialsView setHidden:NO];
[self.whatItCoversView setHidden:YES];
[self.pageControl setCurrentPage:1];
break;
case 2:
[self.usageScenarioView setHidden:YES];
[self.loginCredentialsView setHidden:YES];
[self.whatItCoversView setHidden:NO];
[self.pageControl setCurrentPage:2];
break;
}
}
lastSelectedViewIndex = [segmentedControl selectedSegmentIndex];
[self.view.layer addAnimation:transition forKey:nil];
}
}
Say your 3 views are named as view1, view2, view3. If you want to remove view1 and show view2 or view3, just do the existing code, but change
[self.view.layer addAnimation:transition forKey:nil];
into
[view1.layer addAnimation:transition forKey:nil];
that will animate the view1 only not the whole view. Similarly you can try,
[view2.layer addAnimation:transition forKey:nil];
[view3.layer addAnimation:transition forKey:nil];
more precisely, do like
transition.removedOnCompletion = YES; // force removal of animation when completed.
{
switch ([segmentedControl selectedSegmentIndex]) {
case 0:
[self.usageScenarioView setHidden:NO];
[self.loginCredentialsView setHidden:YES];
[self.whatItCoversView setHidden:YES];
[self.pageControl setCurrentPage:0];
[self.usageScenarioView.layer addAnimation:transition forKey:nil];
break;
case 1:
[self.usageScenarioView setHidden:YES];
[self.loginCredentialsView setHidden:NO];
[self.whatItCoversView setHidden:YES];
[self.pageControl setCurrentPage:1];
[self.loginCredentialsView.layer addAnimation:transition forKey:nil];
break;
case 2:
[self.usageScenarioView setHidden:YES];
[self.loginCredentialsView setHidden:YES];
[self.whatItCoversView setHidden:NO];
[self.pageControl setCurrentPage:2];
[self.whatItCoversView.layer addAnimation:transition forKey:nil];
break;
}
}
lastSelectedViewIndex = [segmentedControl selectedSegmentIndex];
}
you can define a UIView* containerView in IB below your UISegmentedControl* segmentedControl which has the coordinates and position of the views you want.
Then you can inside that view you can do transition between the three UIViews you want with this function:
-(void) replacePreviousViewInContainerViewWith:(UIView*) newView {
[UIView transitionWithView:_containerView duration:0.7
options:UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionCurveEaseIn
animations:^ {
[_containerView.subviews[0] removeFromSuperview];
[_containerView addSubview:newViewVC.view];
}
completion:^(BOOL finished) {
if (finished) {
NSLog(#"Now displaying %#.", [newView class]);
}
}];
[UIView commitAnimations];
}
and you call this function like this from where you want to change the Views:
UIViewController *newViewVC = [[UIViewController alloc] initWithNibName#"YOURNAME"];
newViewVC.view.frame = _containerView.frame;
[self replacePreviousViewInContainerViewWith: newViewVC.view];
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];
}];
}