Edgar Edgar - 5 months ago 25
iOS Question

Custom transition for push animation with NavigationController on iOS 9

I have a custom push transition between view controllers embedded in a

UINavigationController
which is working fine when built with iOS 7/8 but presents a wrong layout when built against iOS 9 SDK.

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

[transitionContext.containerView addSubview:toViewController.view];

...


It then goes on and performs an animation. The issue is that the content of the
toViewController
, even though it is using the right top layout guide auto layout constraint, presents the content behind the navigation bar.

Nevertheless, it works fine on iOS 8 and if we force a redraw (e.g., sending the app to the background and bring it back, present a modal on top and dismiss it, etc) will cause the whole auto layout system to redraw itself and
toViewController
's view jumps to the right place (as being the top layout guide, x pixels from the navigation bar rather than x pixels from the top of the device's screen).

Adding

[self.view setNeedsUpdateConstraints];
[self.view layoutIfNeeded];


Works if put in
viewDidAppear:animated
, but does not work on
viewDidLoad
or
viewWillAppear:animated
. This is not a solution as users would see the view jumping when the redraw is happening at
viewDidAppear:animated

Answer

I've managed to fix my issue by adding the following line before addSubview::

  toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController];

From Apple's Documentation for finalFrameForViewController:

Returns the ending frame rectangle for the specified view controller’s view.

The rectangle returned by this method represents the size of the corresponding view at the end of the transition. For the view being covered during the presentation, the value returned by this method might be CGRectZero but it might also be a valid frame rectangle.