Keeping Swipe Back with UINavigationController Transitions
23 Sep 2015Pretty much everyone uses the Swipe Back feature that you get for free with UINavigationController. But what happens when you add some awesome animated transitions for your push actions? You can’t swipe back anymore!
I found a way around this! Though, it does feel a bit hacky…
tl;dr
You will lose swipe back if you overwrite the delegate of UINavigationController. So, after the animation is complete, return it to what it used to be.
It’s Chicken and Egg Problem
To make a custom transition for a UINavigationController’s push or pop animations, I need to become the navigation controller’s delegate property, so I can provide it with the UIViewControllerAnimatedTransitioning to perform the animation.
The problem is that if I overwrite the navigation controller’s delegate property, I can no longer use the default swipe back function!
Sure, I could go through the effort and reimplement the swipe back functionality using a UISwipeGestureRecognizer and probably a UIPercentDrivenInteractiveTransition…
That sounds like a lot of work, and my custom swipe back could look out of place if Apple ever decides to change that animation.
The Hacky Workaround
All I want to do is provide a custom transition, so really all you need to implement is one method. I don’t care about the rest. I only want this one:
func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
// Some transition from somewhere
return transition
}
Wouldn’t it be nice if I could just intercept that one method? I tried making a wrapper class that returns the transition, and hands the delegate keys back to the whoever was the previous delegate.
func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool) {
// Now that the transition is complete,
// so we'll return delegate to what it used to be
navigationController.delegate = previousDelegate
previousDelegate = nil
}
And that’s really all it took. I get my awesome custom animation, and my users can still swipe back to the previous screen like they would expect.
If you need more context, I made a Sample Project on Github with a working NavigationTransitionController included.
If you use the sample code, you might want to forward some delegate calls to the previousDelegate
– it was unnecessary in the sample, but it probably wouldn’t hurt to be a good citizen.
This is a problem I encountered quite a long time ago. If there is a better, more correct way to fix this, let me know in the comments!