'How do I cross dissolve when pushing views on a UINavigationController in iOS 7?
If I simply call the push method with:
[self.navigationController pushViewController:viewController animated:YES];
then it uses a push animation. How do I change it to use a cross dissolve animation, like I can with a modal segue?
Solution 1:[1]
You can use a CATransition
as demonstrated in this answer:
CATransition* transition = [CATransition animation];
transition.duration = 0.5;
transition.type = kCATransitionFade;
[self.navigationController.view.layer addAnimation:transition forKey:nil];
[self.navigationController pushViewController:viewController animated:NO];
Solution 2:[2]
I use an extension for easy reuse:
extension UINavigationController {
func fadeTo(_ viewController: UIViewController) {
let transition: CATransition = CATransition()
transition.duration = 0.3
transition.type = CATransitionType.fade
view.layer.add(transition, forKey: nil)
pushViewController(viewController, animated: false)
}
}
Notice how animated is false; when you set it to true, you still see the standard 'push' animation (right to left).
Solution 3:[3]
The UINavigationControllerDelegate protocol has a method that can return a custom UIViewControllerAnimatedTransitioning object which will control the animation between the two view controllers involved in the transition.
Create an Animator class to control the cross-dissolve transition:
class Animator: NSObject, UIViewControllerAnimatedTransitioning {
let animationDuration = 0.25
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return animationDuration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
toVC?.view.alpha = 0.0
let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)
transitionContext.containerView.addSubview(fromVC!.view)
transitionContext.containerView.addSubview(toVC!.view)
UIView.animate(withDuration: animationDuration, animations: {
toVC?.view.alpha = 1.0
}) { (completed) in
fromVC?.view.removeFromSuperview()
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
}
And provide it in your UINavigationControllerDelegate:
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return Animator()
}
Here is a more in-depth tutorial: https://web.archive.org/web/20191204115047/http://blog.rinatkhanov.me/ios/transitions.html
Solution 4:[4]
SWIFT 3, 4.2 -- as of October, 2019
let transition = CATransition()
transition.duration = 0.5
transition.type = kCATransitionFade
self.navigationController?.view.layer.add(transition, forKey:nil)
Solution 5:[5]
You can set Push viewcontroller like this.
CATransition* transition = [CATransition animation];
transition.duration = 0.4;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
transition.type = kCATransitionFade;
[self.navigationController.view.layer addAnimation:transition forKey:@"kCATransition"];
[self.navigationController pushViewController:readerViewController animated:false];
You can set Pop viewcontroller like this.
CATransition* transition = [CATransition animation];
transition.duration = 0.4;
transition.timingFunction = [CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionEaseOut];
transition.type = kCATransitionFade;
[self.navigationController.view.layer addAnimation:transition
forKey:@"kCATransition"];
[self.navigationController popViewControllerAnimated:false];
Solution 6:[6]
Update Swift 5
Using prepare(for segue:)
instead of pushViewController:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// other preparations
let transition: CATransition = CATransition()
transition.duration = 0.3
transition.type = CATransitionType.fade
navigationController?.view.layer.add(transition, forKey: nil)
}
Solution 7:[7]
Following michaels answer you can use UINavigationController extension.. something like this
/// Create an animatied fade push with default duration time.
/// - Parameters:
/// - controller: Which controller are we pushing...?
/// - animationDuration: What's the animation duration - has a default
func pushViewControllerWithFade(_ controller: UIViewController, animationDuration: TimeInterval = .Animation.defaultAnimationTime) {
let transition: CATransition = .init()
transition.duration = animationDuration
transition.type = .fade
pushViewController(controller, withTransition: transition)
}
/// Custom transition animation for pushing view contorller
/// - Parameters:
/// - controller: Which controller are we presenting now?
/// - transition: The transition for the push animation
func pushViewController(_ controller: UIViewController, withTransition transition: CATransition) {
view.layer.add(transition, forKey: nil)
pushViewController(controller, animated: false)
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Community |
Solution 2 | |
Solution 3 | Iulian Onofrei |
Solution 4 | |
Solution 5 | kalpesh |
Solution 6 | Olympiloutre |
Solution 7 | Tziki |