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];
}];
}
Related
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.
My code :
navigator.m
- (void)newPushPage:(UIViewController *)controller
{
[self pushViewController:controller animated:YES];
}
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC
{
if (operation == UINavigationControllerOperationPush || operation == UINavigationControllerOperationPop)
{
self.animator = [Animator new];
return self.animator;
}
return nil;
}
animator.m
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
return 0.5;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
[[transitionContext containerView] addSubview:toViewController.view];
toViewController.view.alpha = 0;
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromViewController.view.transform = CGAffineTransformMakeScale(0.1, 0.1);
toViewController.view.alpha = 1;
} completion:^(BOOL finished) {
fromViewController.view.transform = CGAffineTransformIdentity;
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
After the pushPage and the screen appears, there is a problem : are visible all the elements that hide in the code and I can see how elements disappear already seeing the screen. It looks unsightly . There is a way ?
inside animateTransition (id)transitionContext protocol push and pop transition should has to be handled separately as below.
//1.Settings for the fromVC ..
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
CGRect sourceRect = [transitionContext initialFrameForViewController:fromVC];
CGRect finalFrameForVC = [transitionContext finalFrameForViewController:toVC];
//2.Insert the toVC view.
if(pushCondition) {
UIView *container = [transitionContext containerView];
[container insertSubview:toVC.view aboveSubview:fromVC.view];
toVC.view.alpha = 0.5;
toVC.view =
} else if (popCondition ){
UIView *container = [transitionContext containerView];
toVC.view.frame = finalFrameForVC;
toVC.view.alpha = 0.5;
[container addSubview:toVC.view];
[container sendSubviewToBack:toVC.view];
UIView *snapShoot = [fromVC.view snapshotViewAfterScreenUpdates:false];
}
//3.Perform the animation.
[UIView animateWithDuration:1.0
delay:0.0
usingSpringWithDamping:1.0
initialSpringVelocity:6.0
options:UIViewAnimationOptionCurveLinear
animations:^{
//Setup the final parameters of views for push
toVC.view // update final view frame
toVC.view.alpha = 1.0;
//Setup the final parameters of views for pop
snapShoot.frame =
} completion:^(BOOL finished) {
//When the animation is completed call completeTransition with final push value
[snapShoot removeFromSuperview];
//When the animation is completed call completeTransition with final push value
toVC.view.alpha= 1.0;
[transitionContext completeTransition:YES];
}];
Create enum / flag property inside amimator and set it inside navigation controller delegate.
if (operation == UINavigationControllerOperationPush)
{
self.animator = [Animator new];
self.animator.pushPopAnimation = UINavigationControllerOperationPush;
return self.animator;
}
See the apple documentation of snapshotViewAfterScreenUpdates:(BOOL)afterUpdates method in code above.
NSLog(#"started");
[UIView animateWithDuration:2 animations:^{
NSLog(#"inside");
} completion:^(BOOL finished){
NSLog(#"finished");
}];
Here is the output:
2015-09-26 13:17:21.541 Look[9894:1848566] started
2015-09-26 13:17:21.541 Look[9894:1848566] inside
2015-09-26 13:17:21.551 Look[9894:1848566] finished
Note the timestamps are milliseconds apart from "inside" to "finished".'
Here is my full code, with the same result and the animation doesn't run.
This is being done for a custom viewController presentation.
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
// Get the set of relevant objects.
UIView *containerView = [transitionContext containerView];
UIViewController *fromVC = (TJWCameraRollCVC *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
TJWPhotoViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
// Set up some variables for the animation.
CGRect containerFrame = containerView.frame;
CGRect toViewStartFrame = [transitionContext initialFrameForViewController:toVC];
CGRect toViewFinalFrame = [transitionContext finalFrameForViewController:toVC];
CGRect fromViewFinalFrame = [transitionContext finalFrameForViewController:fromVC];
NSMutableArray *oldObjects = [[NSMutableArray alloc] init];
UIImageView *zoomImage;
// Set up the animation parameters.
if (presenting) {
// Modify the frame of the presented view so that it starts
// offscreen at the lower-right corner of the container.
UIImageView *zoomImage = [[UIImageView alloc] initWithImage:toVC.chosenImage];
zoomImage.frame = CGRectMake(toVC.fullScreenOrigin.x, toVC.fullScreenOrigin.y, containerFrame.size.width, containerFrame.size.height);
zoomImage.alpha = 0.2;
zoomImage.transform = CGAffineTransformMakeScale(0.1, 0.1);
[containerView addSubview:zoomImage];
toView.frame = CGRectMake(containerFrame.size.width, containerFrame.size.height, containerFrame.size.width, containerFrame.size.height);
}
else {
// Modify the frame of the dismissed view so it ends in
// the lower-right corner of the container view.
fromViewFinalFrame = CGRectMake(containerFrame.size.width, containerFrame.size.height, toView.frame.size.width, toView.frame.size.height);
}
// Always add the "to" view to the container.
// And it doesn't hurt to set its start frame.
[containerView addSubview:toView];
// Animate using the animator's own duration value.
NSLog(#"started");
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
NSLog(#"inside");
if (presenting) {
NSLog(#"presenting");
// Move the presented view into position.
[zoomImage setFrame:toViewFinalFrame];
[zoomImage setTransform: CGAffineTransformMakeScale(1, 1)];
[zoomImage setAlpha:1];
}
else {
// Move the dismissed view offscreen.
[fromView setFrame:fromViewFinalFrame];
}
} completion:^(BOOL finished){
NSLog(#"finished");
BOOL success = ![transitionContext transitionWasCancelled];
NSLog(#"%d", success);
// After a failed presentation or successful dismissal, remove the view.
if ((presenting && !success) || (!presenting && success)) {
[toView removeFromSuperview];
}
if (presenting && success) {
// toView.frame = containerFrame;
[zoomImage removeFromSuperview];
}
// Notify UIKit that the transition has finished
[transitionContext completeTransition:success];
}];
}
i'm trying to present PopUp ViewController with animation, using UIViewcontrollerAnimatedTransitioning. I've created a Modal Segue from TableViewCell to my Viewcontroller
In PopupPresentAnimationController (that implement UIViewcontrollerAnimatedTransitioning) i have
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
[fromViewController addChildViewController:toViewController];
toViewController.view.frame = fromViewController.view.frame;
[fromViewController.view addSubview:toViewController.view];
[toViewController didMoveToParentViewController:fromViewController];
NSTimeInterval duration = [self transitionDuration:transitionContext];
[UIView animateWithDuration:duration delay:0.0 usingSpringWithDamping:0.6 initialSpringVelocity:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
fromViewController.view.alpha = 0.5;
} completion:^(BOOL finished) {
fromViewController.view.alpha = 1.0;
[transitionContext completeTransition:YES];
}];
}
The PopUpViewController have a background black with 50% opacity and, when it appear all "works" but after animation end, the screen become black.
UPDATE 1:
UIViewController *fromViewController =[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *container = [transitionContext containerView];
CGRect initialFrame = toViewController.view.frame;
initialFrame.origin.y = toViewController.view.frame.size.height;
toViewController.view.frame = initialFrame;
[container insertSubview:toViewController.view aboveSubview:fromViewController.view];
NSTimeInterval duration = [self transitionDuration:transitionContext];
[UIView animateWithDuration:duration delay:0 options:0 animations:^{
CGRect newFrame = toViewController.view.frame;
newFrame.origin.y = 0;
toViewController.view.frame = newFrame;
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
UPDATE 2
In prepareForSegue i have added the follow line
controller.modalPresentationStyle = UIModalPresentationCustom; // controller is the destination
With this line on, the black is no more black!
The problem you're having is that you are adding your toViewController to the fromViewController. When the animation ends, the toViewController is removed and in turn, so is your fromViewController. The proper way to handle this is to use the container view provided by the context:
UIView *container = [transitionContext containerView];
This is an example of how to do a popup transition:
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *container = [transitionContext containerView];
if (!self.beingDismissed) {
//Make controller hidden so it can slide in
CGRect initialFrame = toViewController.view.frame;
initialFrame.origin.y = toViewController.view.frame.size.height;
toViewController.view.frame = initialFrame;
[container insertSubview:toViewController.view aboveSubview:fromViewController.view];
}
[UIView animateKeyframesWithDuration:0.5 delay:0 options:0 animations:^{
if (!self.beingDismissed) {
//Show view controller
CGRect newFrame = toViewController.view.frame;
newFrame.origin.y = 0;
toViewController.view.frame = newFrame;
} else {
//Hide view controller
CGRect newFrame = fromViewController.view.frame;
newFrame.origin.y = fromViewController.view.frame.size.height;
fromViewController.view.frame = newFrame;
}
} completion:^(BOOL finished) {
[transitionContext completeTransition:finished];
}];
}
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
}