Search code examples
iosobjective-cuiviewcontrolleruinavigationcontrollerfacetime

How to recreate FaceTime's navigation view controller transition animation with overlapping transparent view controllers?


Short version:

I want to reproduce FaceTime's navigation transition animation to push transparent view controllers onto the navigation stack without the top view's content overlapping the bottom view's content.

Problem:

In a standard UINavigationController, pushing a destination view controller that has a transparent background results in an unsightly animation: the source view controller dims and translates, then pops out of existence. This is because the standard navigation push animation assumes the destination view controller has completely obscured the source view controller by the end of the animation, an assumption that is violated when the destination view controller's background is transparent. Please see this animation:

undesirable navigation push animation

I have uploaded a demo project that re-produces this behavior at https://github.com/bgfriend0/PushVCWithClearBackground.

Desired effect:

Apple's FaceTime app, however, seems to be able to push a view controller with a clear background while simultaneously masking out the source view controller's content, so that the push animation is clean. Please see this animation:

desired FaceTime navigation push animation

I want to reproduce this FaceTime behavior, but I haven't found a solution.

Research:

Literature on the issue is surprisingly thin. I can find a few questions that touch on it (e.g., Segue Push Animation with Clear Background is Flashing on iOS 7 and Views getting darker when are pushed on navigation controller), but no solution actually resolves the problem by reproducing the desired FaceTime effect.

I found a link in a tweet (https://twitter.com/b3ll/status/384114227884986368) to the Apple dev forums on this issue, but again, no solution was forthcoming: https://devforums.apple.com/message/897379#897379.

Ideas:

The only workable solutions I've come up with are basically the same as those put forth by Caleb Davenport in the Apple forum post:

It's got to be one of three things:

(1) They are masking the left view up to the frame of the right view.

(2) They are copying the background contents into the right view while offsetting it such that the right view isn't really transparent.

(3) They are running custom view transitions.

Each of these has some degree of merit, but they're all quite complicated and I can't help but hope there should be, say, some convenient little flag Apple is taking advantage of to produce the desired mask effect. Of course, even if such a flag exists, it may be private API... nevertheless, I'm posting this question to see if anyone out there has come up with or can come up with an elegant solution to reproduce the desired FaceTime transparent push animation.


Solution

  • For anyone who may be interested, I did find an answer to this question in that Apple uses a private flag clipUnderlapWhileTransitioning on the UINavigationController and _UINavigationParallaxTransition classes.

    See, e.g.,:

    https://github.com/JaviSoto/iOS8-Runtime-Headers/blob/master/Frameworks/UIKit.framework/UINavigationController.h

    https://github.com/JaviSoto/iOS8-Runtime-Headers/blob/master/Frameworks/UIKit.framework/_UINavigationParallaxTransition.h

    With this flag, I was able to completely reproduce the Facetime effect.

    Naturally, all the usual caveats about using private API apply to this case (i.e., this is NOT allowed for Apps you intend to submit to the App Store).