1

For some context, I recommend reading this:

Very relevant question: "From View Controller" disappears using UIViewControllerContextTransitioning Very relevant answer: https://stackoverflow.com/a/25901154/751268


I'm trying to implement a custom view controller transition that animates the new view controller to cover half the screen, while simultaneously shrinking the presenting view controller to 90% (centered in the window, underneath the presented view controller).

First, my problem was that viewFromKey: returned nil. To solve that, the answer mentioned:

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.

I did that, and viewFromKey: doesn't return nil anymore, but now the presenting view controller disappears completely (which makes sense considering I explicitly say it should by implementing shouldRemovePresentersView).

I add the presenting view controller's view to the container view, but it still gets removed. Is there anything else I should be doing to get this working?

Here's some relevant code:

UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];

BOOL show = self.isPresentation;
UIView *menuView = show ? toView : fromView;
UIView *backView = show ? fromView : toView;

UIView *containerView = [transitionContext containerView];

[containerView addSubview:backView];
[containerView addSubview:dimmedView];
[containerView addSubview:menuView];

// Adjust transforms, alpha and perform animations

I thought that by returning YES from shouldRemovePresentersView and manually adding it to the containerView, that should fix the issue, but backView gets removed anyway...

Community
  • 1
  • 1
Scott Berrevoets
  • 16,921
  • 6
  • 59
  • 80
  • I'm not sure if it'll help you, but I just watched a WWDC session the other day covering this type of presentation complete with a demo. "A look inside Presentation Controllers" it's "session 228" available on the WWDC app. –  Nov 24 '14 at 17:46
  • I've watched that before, though I don't remember hearing about that specifically. I'll re-watch it anyway, though. – Scott Berrevoets Nov 26 '14 at 03:56
  • Did you manage to find a solution to this? I'm struggling with this and if it's a bug, I'm surprised that it hasn't been fixed yet. – Andrea Apr 06 '17 at 14:27

2 Answers2

5

I'm adding another answer, as my response is too long to fit in a comment.

First of all the viewForKey is available in iOS8, so unless you are targeting iOS8 only (why?) you should not use it, or use it after checking that the UIViewControllerContextTransitioning responds to that selector and use the viewControllerForKey for iOS7.

With that being said, it seems to me that this is a bug and I explain my self:

If you look at the UIPresentationController header file, you will see that it says

// Indicate whether the view controller's view we are transitioning from will be removed from the window in the end of the
// presentation transition
// (Default: YES)
- (BOOL)shouldRemovePresentersView;

So as you see the default is YES, so this should only overriden when you specifically want to say NO. However, you are right, without this being set explicitly to YES, the viewForKey for the UITransitionContextFromViewControllerKey remains nil.

I think you should fill in a bug report for this and for now use the viewControllerForKey which is fine to be used (nothing wrong with this) as it's not deprecated and works in both OS versions without a problem.

And the reason this is most likely a bug is that the viewForKey should return a view for theUITransitionContextFromViewControllerKey when the shouldRemovePresentersView is explicitly set to NO and not YES.

My 2 cents

Lefteris
  • 14,550
  • 2
  • 56
  • 95
  • 1
    It's not the clearest API and it's especially hard if you're not sure whether certain behavior is due to a bug in iOS 8 or in your own code, but this does make sense. Thanks! – Scott Berrevoets Dec 01 '14 at 19:32
2

Why are you setting shouldRemovePresentersView to YES if what you want is that the fromView remains visible?

I had the same problem as you, as in my custom 3d presentation the parent view controller was removed as soon as the transition was completed.

The solution there is to change the modalPresentationStyle to UIModalPresentationOverCurrentContext This will specifically prohibit the system to remove the owner viewController when the transition ends.

Still my 3D transition suffered from animation issues, when using this approach.

Si I ended up using a UIModalPresentationCustom modalPresentationStyle which solved almost all my issues, with the exception that the new view controller (which was a UINavigationController) would not move down when the in-call status bar would appear.

To solve this, I ended up changing the modalPresentationStyle back to UIModalPresentationFullScreen after the transition had been completed.

Here is my code that shows the new viewController with the custom presentation:

//show Login Screen
LoginViewController *viewController = [[LoginViewController alloc] initWithNibName:@"LoginViewController" bundle:nil];

UINavigationController *loginNavController = [[UINavigationController alloc] initWithRootViewController:viewController];
loginNavController.interactivePopGestureRecognizer.enabled = YES;
loginNavController.transitioningDelegate = self.customTransitionDelegate;
loginNavController.modalPresentationStyle = UIModalPresentationCustom;

[navigationController presentViewController:loginNavController animated:YES
                                 completion:^{
                                     //important, else status bar is not moving entire screen down....
                                     loginNavController.modalPresentationStyle = UIModalPresentationFullScreen;
                                 }
 ];

Also very important is that you must NOT add the old view to the containerView when you run your dismissal animation

So if the animation is the presenting, add your toView to the containerView

    UIView* inView = [transitionContext containerView];

    UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
.....
......

    [inView insertSubview:toViewController.view aboveSubview:fromViewController.view];

But on the dismissal presentation DO NOT ADD the view to the containerView as it is still showing (since we specifically asked the system NOT to remove it), but simply animate between the two views.

Lefteris
  • 14,550
  • 2
  • 56
  • 95
  • The reason for returning `YES` from `shouldRemovePresentersView` is that that will cause `viewForKey:` to not return `nil`. I'm looking for an iOS 8 solution here, and the docs specifically state that we should use `viewForKey:` if we need access to one of the view controllers' views, not `viewForViewController:` (which is what you're doing). In iOS 7 this was fine, but I'm looking for how this should be done in iOS 8. – Scott Berrevoets Nov 26 '14 at 19:24
  • Well I'm testing this on iOS8 and it works fine. Also [the documentation](https://developer.apple.com/library/ios/documentation/uikit/reference/UIViewControllerContextTransitioning_protocol/index.html#//apple_ref/occ/intfm/UIViewControllerContextTransitioning/viewControllerForKey:) doesn't seem to indicate that we shouldn't be using the ViewController directly. – Lefteris Nov 26 '14 at 22:28
  • UIViewControllerTransitioning.h: "Animators should not directly manipulate a view controller's views and should use viewForKey: to get views instead." We can still use `viewControllerForKey:`, but not when we just want its view. – Scott Berrevoets Nov 27 '14 at 00:17
  • I also tried changing the modalpresentationStyle in the completion block and it works for me on iOS 9, but it does not on iOS 8.. Any ideas? – JarnoV May 25 '16 at 15:25