Customise UINavigationController animation: CATransition - iphone

I have an UINavigationController. I made VC1 the rootViewController and programatically load VC2 from VC1 and then have the custom animation to go from VC1 to VC2. Standard. Everything is fine and good.
Now, I would like to have a custom animation between the two like so:
In sum, VC1 slides out of view while VC2 is beneath it. Just like a stack of paper where you slide away the first sheet (VC1) and thus reveal the sheet beneath (VC2).
So what I tried is the following code which is called from VC1 in order to get to VC2. But there are problems with it:
MyVC2 *vctwo = [[[MyVC2 alloc] init] autorelease];
CATransition *transition = [CATransition animation];
transition.duration = 1;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionReveal;
transition.subtype = kCATransitionFromRight;
transition.delegate = self;
[self.navigationController.view.layer addAnimation:transition forKey:nil];
[[self navigationController] pushViewController:vctwo animated:YES];
The problems are the following:
VC2 is not fixed in the background. It kind of slides in as well (even though I specified kCATransitionReveal). I want to be VC2 totally fixed in the background.
VC1 fades out. I don't really know why. I did not use kCATransitionFade or the like, so I can't see where the fade comes from.
Any suggestions why I am not getting the expected results would be very much appreciated. Sorry if this is obvious, but I am trying this for hours now and got really frustrated.

I think you should use [[self navigationController] pushViewController:vctwo animated:NO]; rather than animated:YES.

I suppose the only way to do this is to forget UINavigationController and come up with my own mechanism to handle everything:
This blog explains how...
So if you wanted to custom animations between VCs, do not use UINavigationController. Here is sample code to swap between two VCs as described above:
-(void)slideDoorOpenTo:(UIViewController *)aController duration:(float)aDuration {
[aController viewWillAppear:YES];
[activeController viewWillDisappear:YES];
//aController.view.alpha = 0.0f;
[self.view insertSubview:aController.view belowSubview:activeController.view]; // so that it is below activeController
[aController viewDidAppear:YES];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:aDuration];
aController.view.transform = CGAffineTransformMakeTranslation(0,0);
activeController.view.transform = CGAffineTransformMakeTranslation(-320,0);
[UIView commitAnimations];
[self performSelector:#selector(animationDone:) withObject:aController afterDelay:aDuration];
}

Related

UINavigationController popViewControllerAnimated: crash in iOS 6

The code below works fine in iOS 4 and 5 but crashes in iOS 6 with EXC_BAD_ACCESS. I'd appreciate any help in troubleshooting it. This code is being called in a UITableViewController that handles my app's search logic:
CATransition *transition = [CATransition animation];
transition.duration = 0.3f;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
transition.type = kCATransitionFade;
[self.navigationController.view.layer addAnimation:transition forKey:nil];
[self.navigationController popViewControllerAnimated:NO];
The way I add the tableView is similar and doesn't crash when called:
SearchTVC *searchTable = [[SearchTVC alloc] init];
searchTable.detailViewController = self.detailViewController;
CATransition *transition = [CATransition animation];
transition.duration = 0.3f;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
transition.type = kCATransitionFade;
[self.navigationController.view.layer addAnimation:transition forKey:nil];
[self.navigationController pushViewController:searchTable animated:NO];
What could be the problem?
*EDIT
Interestingly the crash doesn't occur if I use [self.navigationController popViewControllerAnimated:YES]; (YES rather than NO). But of course this defeats the purpose of using a custom pop animation.
Check whether you have a line like the following somewhere in your view controller code:
self.navigationController.delegate=self;
If so, then you must set it back
self.navigationController.delegate=nil;
before you say
[self.navigationController popViewControllerAnimated:YES];
Otherwise, popViewControllerAnimated will first deallocate the delegate and then try to call it - resulting in a crash.
I know my question was vague, but I didn't have much else to go off of. I knew the line [self.navigationController popViewControllerAnimated:NO]; was the problem but I couldn't figure out why. Then I came across this question and the first answer suggested I make my search table an instance variable rather than creating a new one every time I want to present it, and that actually worked. It must be a memory issue that I can't wrap my head around.
tl;dr :
Make sure the UIViewController that's being pushed and popped is an instance variable.
Though super late to party... Hope this might help someone in future.
I opened a very old code...
Enabling ARC mode and then resolving all the compiler warnings/error fixed it automatically.

TabBar always animates to the left when pushing a new viewController

I have a Tab Bar app with a navigation controller on one tab.
I want to push a new view controller, but have it animate in from the left.
What I have is this:
CATransition *transition = [CATransition animation];
transition.duration = 0.8;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;
transition.delegate = self;
controller.hidesBottomBarWhenPushed = YES;
[self.navigationController.view.layer addAnimation:transition forKey:nil];
[self.navigationController pushViewController:controller animated:YES];
Everything pushes in from the left, except for the tab bar, which always slides out to the left (and transition.duration has no effect on that either).
Is there a way to get the taBar to slide out in the same direction & speed with the rest of the view?
(I've also tried using pushModalViewController, but that has various graphical glitches instead).
It seems to me that you are trying to animate only self.navigationController in your code; since the UITabBar is outside of it, it seems reasonable that it does not get animated as you like (I think it gets animated only as an after effect of animating the inner view, but you have no control on it).
What I would try to do is accessing your UITabBarController's view and add an animation to its CALayer as well (or exclusively to that, you can try different possibilities).

When popping to a previous viewcontroller I get a duplicate of itself during the transition

I am using the following code to popping to a parent view controller (not always the direct parent in the stack), but for some reason I get the current view controller slide in over itself before the parent I am popping to.
CATransition* transition = [CATransition animation];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;
[self.navigationController.view.layer addAnimation:transition forKey:kCATransition];
[self.navigationController popToViewController:vc animated:YES];
hasPopped = YES;
This appears to be down to the animation code Im using. The only reason I am setting this is because when the app is rotated to landscape the views come in from the bottom on the side. As raised in this question: https://stackoverflow.com/questions/4102345/why-do-views-slide-out-at-the-bottom-when-landscape
I solved this by turning the animated:off during the pop as the assigned animation was providing it

iPhone SDK: Optimize UIModalTransitionStyleFlipHorizontal

I want to flip my new ModalView with very high performance, but the new View has a lot of subviews so the performance of the Flip-Effect is very bad. Actually i do it with:
[controller setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[self presentModalViewController:backSideController animated:YES];
I also tried it with
CATransition *transition = [CATransition animation];
transition.duration = 0.75;
[transition setType: #"flip"];
[transition setSubtype:#"fromRight"];
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[transition setFillMode:#"extended"];
[[self.view layer] addAnimation:transition forKey:nil];
[self.view addSubview: backSideController.view];
[CATransaction commit];
With Core-Animation it works a little bit faster ... ware there further ways to opimize this task? e.g. Adding view when animation stops and just flipping a screenshot until animation stops?
Try accessing backsideController.view before you begin the animations. This will cause backsideController's loadView and viewDidLoad to be called. I'm guessing that this is your performance hit -- that all that loading & allocating is causing the animation to stutter.
You don't need anything fancy, you can do something like:
if (backsideController.view == nil)
NSLog(#"Where's my view?!");
before your other code, above.
I don't believe that having many-many subviews causes performance problems on the flip; I'm pretty sure (without looking at your code or checking in instruments, which you should do!) that the problem is the time it takes to load and allocate the view components.
Also, I'd stick with presentModalViewController, if it does what you want. Having all that extra code in your 2nd example -- unless it's needed for functionality -- is just maintenance headache.

Flip transition in iPhone

I am facing problems in flipping views in iPhone.
I have two views in appDelegate. I want to flip them once user clicks on a button.
I have the following code:
CATransition *transition = [CATransition animation];
transition.duration = 0.75;
[transition #"twist"];
[transition setSubtype:#"fromRight"];
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[transition setFillMode:#"extended"];
[[window layer] addAnimation:transition forKey:nil];
[window addSubview:self.s.view];
[CATransaction commit];
But this is not working. Do anybody knows a better way to flip the views on window side.
What I am doing is calling the method from appDelegate in the respective viewControllers to flip the views.
If you're using the 3.0 SDK and all you want is a simple flip transition (ala the Weather app) then you don't need to go down to CATransition. The higher-level UIView animation transitions will do what you want but with 3.0 there is an even easier way: simply present your new view as a modal view controller and set the modal transition style to flip. From within the first controller:
UIViewController *controllerForSecondView = ..;
controllerForSecondView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controllerForSecondView animated:YES];
Flip back again by using dismissModalViewController.
Documentation Reference
#Luke - thanks, this sample helped me...1 correction though (based on UIViewController.h)
UIViewController *controllerForSecondView = ..;
controllerForSecondView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controllerForSecondView animated:YES];
From the header file comments:
// Defines the transition style that will be used for this view controller when it is presented modally. Set this property on the view controller to be presented, not the presenter.
// Defaults to UIModalTransitionStyleSlideVertical.
#property(nonatomic,assign) UIModalTransitionStyle modalTransitionStyle
See The Elements sample code. Particularly AtomicElementViewController -flipCurrentView.