Animation not always visible from animateTransition in UIViewControllerAnimatedTransitioning - ios

I am using some UIViewControllers contained in a UINavigationController.
One is presented with a push segue from a UIButton on the storyboard, and is then dismissed using a swipe gesture which calls
popViewControllerAnimated
I'm using a UINavigationControllerDelegate to provide a custom object which conforms to UINavigationControllerDelegate. The code for animateTransition is shown below.
My problem is that the first time this runs, the view animates in when presenting, but every time after that, it doesn't animate (it just appears instantly).
Can anyone help?
Thanks!
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
//Get references to the view hierarchy
UIView *containerView = [transitionContext containerView];
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
if (self.isPresenting) {
[containerView insertSubview:toViewController.view belowSubview:fromViewController.view];
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromViewController.view.transform = CGAffineTransformMakeTranslation(-1000, 0);
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
} else if (!self.isPresenting) {
//Add 'to' view to the hierarchy
[containerView insertSubview:toViewController.view belowSubview:fromViewController.view];
//Scale the 'from' view down until it disappears
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
//toViewController.view.transform = CGAffineTransformMakeScale(1.0, 1.0);
fromViewController.view.transform = CGAffineTransformMakeScale(0.01, 0.01);
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}
}

I ran into a similar set of issues after upgrading to iOS8. A few things to check:
Make sure to call these before starting the animations:
[toViewController beginAppearanceTransition:YES animated:YES];
[fromViewController beginAppearanceTransition:NO animated:YES];
Then call these in the completion block:
[toViewController endAppearanceTransition];
[fromViewController endAppearanceTransition];
[transitionContext completeTransition:finished];
The above is needed to ensure viewWillAppear: and viewWillDisappear: are called at the right times.
Set modalPresentationStyle = UIModalPresentationFullScreen, instead of UIModalPresentationCustom (which behaves very differently).
Hope this helps.
Cheers!

Be careful setting self.navigationController.delegate
you probably place it in somewhere that only runs once like ViewDidLoad
try placing it in ViewDidAppear
override func viewDidAppear(animated: Bool)
{
super.viewDidAppear(animated)
self.navigationController.delegate = self
}

Related

Custom Modal transition not working - example

I am trying to get UIViewControllerContextTransitioning working.
What I want:
What I would like to have, is presenting modal view controller with custom animation and transparent background.
Wthat I did:
I created animator implementing UIViewControllerTransitioningDelegate,
Set for modal controller:
self.modalPresentationStyle = UIModalPresentationCustom;
self.transitioningDelegate = self;
What I achieved so far,
Modal controller animates presenting and dismissing view correctly, but after dismissing is finished, entire app becomes black. I used xCode tool to pick what's in window hierarchy, and there is nothing. My guess is, that I changed VC's superview when adding to context's container.
Animator
#implementation AlertAnimator
const static CGFloat kAnimationDuration = 1.2;
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController *to = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *from = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
if (self.transitionType == ModalAnimatedTransitioningTypePresent) {
[self animatePresentingInContext:transitionContext toVC:to fromVC:from];
} else if (self.transitionType == ModalAnimatedTransitioningTypeDismiss) {
[self animateDismissingInContext:transitionContext toVC:to fromVC:from];
}
}
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext{
return kAnimationDuration;
}
- (void)animatePresentingInContext:(id<UIViewControllerContextTransitioning>)transitionContext toVC:(UIViewController *)toVC fromVC:(UIViewController *)fromVC {
CGRect fromVCRect = [transitionContext initialFrameForViewController:fromVC];
CGRect toVCRect = fromVCRect;
toVCRect.origin.y = toVCRect.size.height;
toVC.view.frame = toVCRect;
UIView *container = [transitionContext containerView];
[container addSubview:fromVC.view];
[container addSubview:toVC.view];
[UIView animateWithDuration:kAnimationDuration animations:^{
toVC.view.frame = fromVCRect;
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
- (void)animateDismissingInContext:(id<UIViewControllerContextTransitioning>)transitionContext toVC:(UIViewController *)toVC fromVC:(UIViewController *)fromVC {
CGRect fromVCRect = [transitionContext initialFrameForViewController:fromVC];
fromVCRect.origin.y = fromVCRect.size.height;
UIView *container = [transitionContext containerView];
[container addSubview:toVC.view];
[container addSubview:fromVC.view];
[UIView animateWithDuration:kAnimationDuration animations:^{
fromVC.view.frame = fromVCRect;
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
#end
Code example:
https://www.dropbox.com/s/oaghtgwvga4nxs4/Test.zip?dl=0
Question:
What I am doing wrong and why screen becomes black?
if this is a presenting animator you should not add the fromVC.view as a subview because its already there. This will cause it to bug out. Try it out and tell me what happens. If its a dismissal animator you shouldn't add the presentingViewController to the hierarchy either.

iOS7 Custom transition called twice

I want to animate the transitions between the tabs of a UITabBarController. For some reason this method from the UIViewControllerAnimatedTransitioning protocol is called twice:
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
The stacktrace:
And immediately after:
Also the - (void)animationEnded:(BOOL)transitionCompleted method is called twice.
I have no ideea why.
This is the code:
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
UIView *containerView = [transitionContext containerView];
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
[toVC.view setTransform:CGAffineTransformMakeTranslation(320, 0)];
[containerView addSubview:toVC.view];
[UIView animateWithDuration:0.5 animations:^{
[toVC.view setTransform:CGAffineTransformIdentity];
[fromVC.view setTransform:CGAffineTransformMakeTranslation(-320, 0)];
} completion:^(BOOL finished) {
[fromVC.view setTransform:CGAffineTransformIdentity];
[toVC.view setTransform:CGAffineTransformIdentity];
[transitionContext completeTransition:YES];
}];
}
For a first impression I believe - (void)animationEnded:(BOOL)transitionCompleted is called twice because you call [transitionContext completeTransition:YES] , which based on apple docs:
Call this method after your animations have completed to notify the system that the transition animation is done. The default implementation of this method calls the animator’s animationEnded: method to give it a chance to perform any last minute cleanup.
And if UIViewControllerAnimatedTransitioning is called twice then here you go.
For further analysis of why UIViewControllerAnimatedTransitioning is called twice i would be happy to see your animationControllerForPresentedController and animationControllerForDismissedController implementation.

How to use UIViewControllerAnimatedTransitioning with UINavigationController?

How can I get custom transitions (iOS7) when pushing a view controller onto UINavigationController? I tried setting the TransitioningDelegate both in the UINavigationController and also on the controller I'm pushing
The methods never get called.
All examples I find use custom transitions when presenting modally.
#rounak has the right idea, but sometimes it helps to have code ready without having to download from github.
Here are the steps that I took:
Make your FromViewController.m conform to UINavigationControllerDelegate. Other sample code out there tells you to conform to UIViewControllerTransitioningDelegate, but that's only if you're presenting the ToViewController.
#interface ViewController : UIViewController
Return your custom transition animator object in the delegate callback method in FromViewController:
- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC {
TransitionAnimator *animator = [TransitionAnimator new];
animator.presenting = (operation == UINavigationControllerOperationPush);
return animator;
}
Create your custom animator class and paste these sample methods:
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return 0.5f;
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
// Grab the from and to view controllers from the context
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
// Set our ending frame. We'll modify this later if we have to
CGRect endFrame = CGRectMake(80, 280, 160, 100);
if (self.presenting) {
fromViewController.view.userInteractionEnabled = NO;
[transitionContext.containerView addSubview:fromViewController.view];
[transitionContext.containerView addSubview:toViewController.view];
CGRect startFrame = endFrame;
startFrame.origin.x += 320;
toViewController.view.frame = startFrame;
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromViewController.view.tintAdjustmentMode = UIViewTintAdjustmentModeDimmed;
toViewController.view.frame = endFrame;
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}
else {
toViewController.view.userInteractionEnabled = YES;
[transitionContext.containerView addSubview:toViewController.view];
[transitionContext.containerView addSubview:fromViewController.view];
endFrame.origin.x += 320;
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
toViewController.view.tintAdjustmentMode = UIViewTintAdjustmentModeAutomatic;
fromViewController.view.frame = endFrame;
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}
}
Essentially, the animator is the object doing the heavy lifting. Of course, you can make your UINavigationControllerDelegate be a separate object, but that depends on how your architect your app.
objc.io's post on view controller transitions are specifically for pushing and popping view controllers. http://objc.io/issue-5/view-controller-transitions.html
I've done this animation (http://i.imgur.com/1qEyMu3.gif) solely based on the objc.io post.
In short you have to have a class(es) implementing UINavigationControllerDelegate, and UIViewControllerAnimatedTransitioning with the required methods for returning the correct animator, and performing the animations.
You can look at my demo project which demonstrates using custom transitions in UINavigationController. Look at https://github.com/Vaberer/BlurTransition.
EDIT: Just realised this might not answer your question. But it is an alternative.
If you're using a storyboard you can do a custom transition by creating a custom segue.
In the attributes inspector change the segue class name to your custom transition class e.g. MySegue. Then create the MySegue class and implement the -(void)perform method to perform your transition.
- (void) perform{
UIViewController *source = self.sourceViewController;
UIViewController *destination = self.destinationViewController;
[UIView transitionFromView:source.view
toView:destination.view
duration:0.50f
options:UIViewAnimationOptionTransitionFlipFromTop
completion:nil];
}

Touches not recognized after custom transition

I have an issue with custom transitions on the iPad. I create a custom transition that animates correctly and seems to work (i.e. the transition occurs). However, when I arrive at the destination view controller (after executing the isLoggedIn block), the destination view controller is unresponsive (it doesn't respond to touch events). II have a feeling it has something to do with the call to [container insertSubview:toViewController.view belowSubview:fromViewController.view]; because if I call [container insertSubview:toViewController.view aboveSubview:fromViewController.view]; the touches work as expected (but you cannot see the animation, as it happens on the source view controller).
Any idea why the touch events aren't being recognized?
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *container = [transitionContext containerView];
//Prepare the view
if (self.isLoggedIn) {
//Insert the main view under the login view
CGRect frame = CGRectMake(0, 0, toViewController.view.frame.size.height,
toViewController.view.frame.size.width);
toViewController.view.frame = frame;
[container insertSubview:toViewController.view belowSubview:fromViewController.view];
} else {
CGRect frame = CGRectMake(0, 0, toViewController.view.frame.size.height,
toViewController.view.frame.size.width);
toViewController.view.frame = frame;
if([toViewController respondsToSelector:#selector(openWalls)]) {
[(DJVLoginViewController*)toViewController openWalls];
}
if([toViewController respondsToSelector:#selector(toggleLoginViewsAlpha:)]) {
[(DJVLoginViewController*)toViewController toggleLoginViewsAlpha:0];
}
//Insert the login view above the main view
[container insertSubview:toViewController.view aboveSubview:fromViewController.view];
}
//Make animations
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
if (self.isLoggedIn) {
//Perform animation
} else {
//Perform animation
}
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}
Try to remove fromView from superview:
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
if (self.isLoggedIn) {
//Perform animation
} else {
//Perform animation
}
} completion:^(BOOL finished) {
[fromViewController.view removeFromSuperview];
[transitionContext completeTransition:YES];
}];
}

ios 7 custom transition not working for navigation controller

I'm testing around the new iOS 7 custom transition API but i have some troubles with the navigation controller case. I tried a very basic test for the moment with this :
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
[transitionContext completeTransition:YES];
}
As you guessed, this code do nothing except to complete the transition with no animations.
But here's the problem : if it's working normally with present / dismiss a controller, all i see with push and pop methods is a black screen, as if [transitionContext completeTransition:YES] didn't work.
I've set all the delegate properties and delegate methods properly, since this method is called all the time (present, dismiss, push, pop).
Did someone already face this issue ?
Try something more like this, I was having trouble with it as well and this helped make more sense of it
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
// 1. obtain state from the context
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
CGRect finalFrame = [transitionContext finalFrameForViewController:toViewController];
// 2. obtain the container view
UIView *containerView = [transitionContext containerView];
// 3. set initial state
CGRect screenBounds = [[UIScreen mainScreen] bounds]; toViewController.view.frame =
CGRectOffset(finalFrame, 0, screenBounds.size.height);
// 4. add the view
[containerView addSubview:toViewController.view];
// 5. animate
NSTimeInterval duration = [self transitionDuration:transitionContext];
[UIView animateWithDuration:duration animations:^{
toViewController.view.frame = finalFrame;
} completion:^(BOOL finished) {
// 6. inform the context of completion
[transitionContext completeTransition:YES];
}];
}
Source: http://www.raywenderlich.com/forums/viewtopic.php?f=37&t=8851

Resources