I got one problem and i have described it below.
I am using UIViewControllerContextTransitioning
for custom transitions.
I have 2 view controllers, first view controller and second view controller.
Now I want to add second view controller on first view controller with an animation. I have achieved it, now the second view controller is transparent, so we can see first view controller below second view controller.
But I am not able to see first view controller, and I can see only black screen below second view controller.
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
self.transitionContext = transitionContext;
if(self.isPresenting){
[self executePresentationAnimation:transitionContext];
}
else{
[self executeDismissalAnimation:transitionContext];
}
}
-(void)executePresentationAnimation:(id<UIViewControllerContextTransitioning>)transitionContext{
UIView* inView = [transitionContext containerView];
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
CGRect offScreenFrame = inView.frame;
offScreenFrame.origin.y = inView.frame.size.height;
toViewController.view.frame = offScreenFrame;
toViewController.view.backgroundColor = [UIColor clearColor];
fromViewController.view.backgroundColor = [UIColor clearColor];
inView.backgroundColor = [UIColor clearColor];
[inView insertSubview:toViewController.view aboveSubview:fromViewController.view];
// [inView addSubview:toViewController.view];
CFTimeInterval duration = self.presentationDuration;
CFTimeInterval halfDuration = duration/2;
CATransform3D t1 = [self firstTransform];
CATransform3D t2 = [self secondTransformWithView:fromViewController.view];
[UIView animateKeyframesWithDuration:halfDuration delay:0.0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0f relativeDuration:0.5f animations:^{
fromViewController.view.layer.transform = t1;
}];
[UIView addKeyframeWithRelativeStartTime:0.5f relativeDuration:0.5f animations:^{
fromViewController.view.layer.transform = t2;
}];
} completion:^(BOOL finished) {
}];
[UIView animateWithDuration:duration delay:(halfDuration - (0.3*halfDuration)) usingSpringWithDamping:0.7f initialSpringVelocity:6.0f options:UIViewAnimationOptionCurveEaseIn animations:^{
toViewController.view.frame = inView.frame;
} completion:^(BOOL finished) {
[self.transitionContext completeTransition:YES];
}];
}
When [self.transitionContext completeTransition:YES];
called, suddenly the first view controller disappears and black screen displays below second view controller.
Does any one have idea ? Thanks.
I was having the same problem here – looks like a bug in iOS 8. I've filed a radar.
I used Reveal to inspect the view hierarchy after the screen goes black. The key UIWindow
is completely empty – no view hierarchy at all!
I played around a bit and it looks like there is an easy workaround, for simple cases. You can just re-add the toViewController
's view as a subview of the key window's:
transitionContext.completeTransition(true)
UIApplication.sharedApplication().keyWindow!.addSubview(toViewController.view)
I've checked and the key window's rootViewController
is still correctly set, so that's fine. I'm not sure what would happen if you presented your controller from within an already presented modal controller, so for more complex cases, you'll have to experiment around.
I feel like the reasoning behind this should be explained better.
The view disappears because you take out the presenting view controller's view out of its original location (view hierarchy), put it inside the containerView that your animator provides but never returns it back after the animation has finished. So that view controller's view is removed with its superview (containerView) from the window completely.
In iOS 7 the system always returned view controllers' views that are involved in the presentation (presenting and presented) to their original places after the transition has finished animating automatically. That no longer happens for some presentation styles in iOS 8.
The rule is very simple: the animator should only manipulate the presenting view controller's view if that view controller's view is going to be hidden (removed from the view hierarchy) completely by the end of transition. In other words it means that after the initial presentation animation finishes only the presented view controller's view will be visible and not the presenting view controller's view. For example if you set presented view controller's view's opacity to 50% and use UIModalPresentationFullScreen you will not be able to see presenting view controller's view underneath the presented but if you use UIModalPresentationOverFullscreen - you will (UIPresentationController's shouldRemovePresentersView
method is responsible for specifying that).
Why not allow the animator manipulate the presenting view controller's view at all times? First of all, if the presenting view controller's view is going to stay visible after the animation finishes during the whole presentation life cycle there is no need to animate it at all — it just stays where it is. Second, if the ownership for that view controller is transferred to the presentation controller, the presentation controller will most likely not know how to layout that view controller's view when needed for example when the orientation changes, but the original owner of the presenting view controller does.
In iOS 8 viewForKey:
method was introduced to get views that the animator manipulates. First, it helps to follow the rule described above by returning nil whenever the animator should not touch the view. Second, it may return a different view for the animator to animate. Imagine that you are implementing a presentation similar to form sheet. In this case you would want to add some shadow or decoration around the presented view controller's view. The animator will animate that decoration instead and the presented view controller's view will be a child of the decoration.
viewControllerForKey:
doesn't go away, it can still be used if a direct access to view controllers is needed but the animator should not make any assumptions about the views it needs to animate.
There are several things you can do to correctly fix an issue with a disappearing presenting view controller's view when you explicitly place it inside the animator's container view:
If do not need to animate the presenting view controller's view, use viewForKey:
to get views to animate instead of reaching out to view controller's views directly. viewForKey:
may return nil or even completely different views.
If you want to animate the presenting view controllers's view you should consider using UIModalPresentationFullScreen
style or continue using UIModalPresentationCustom
and implement your own subclass of UIPresentationController with shouldRemovePresentersView
returning YES
. In fact, the implementation of this method is the main difference between internal presentation controllers defined by UIModalPresentationFullScreen
and UIModalPresentationCustom
styles apart from the fact that the latter allows you to use custom presentation controllers.
In all other rare cases you will have to return the presenting view controller's view to its original location as other answers suggested.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With