I have been searching for how to transition/animate the barTintColor of a UINavigationBar for a while now, and I only see different answers. Some use UIView.animateWithDuration, some use CATransition, but the most interesting ones, like this one use animate(alongsideTransition animation.., which I like the sound of, but I can't get it working properly. Am I doing something wrong?
Many specify that I can simply use the transitionCoordinator in viewWillAppear:. I have set up a fresh super tiny project like this:
class RootViewController:UIViewController{ //Only subclassed override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) transitionCoordinator?.animate(alongsideTransition: { [weak self](context) in self?.setNavigationColors() }, completion: nil) } func setNavigationColors(){ //Override in subclasses } } class FirstViewController: RootViewController { override func viewDidLoad() { super.viewDidLoad() self.title = "First" } override func setNavigationColors(){ navigationController?.navigationBar.barTintColor = UIColor.white navigationController?.navigationBar.tintColor = UIColor.black navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.black] navigationController?.navigationBar.barStyle = UIBarStyle.default } } class SecondViewController: RootViewController { override func viewDidLoad() { super.viewDidLoad() self.title = "Second" } override func setNavigationColors(){ navigationController?.navigationBar.barTintColor = UIColor.black navigationController?.navigationBar.tintColor = UIColor.white navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white] navigationController?.navigationBar.barStyle = UIBarStyle.black } } - The push-transition from
FirsttoSecondlooks perfect. All elements transition perfectly, maybe except the StatusBar, which instantly changes to white. I'd rather know how to transition it, but I'll accept it for now. - The pop-transition from
SecondtoFirstis completely wrong. It keeps the colors fromSeconduntil the transition is completely done. - The drag-transition from
SecondtoFirstlooks alright, when dragging all the way over. Again, the StatusBar instantly becomes black as soon as I start dragging, but I don't know if that's possible to fix. - The drag-transition from
SecondtoFirstbut cancelled mid-drag and returning toSecondis completely screwed up. It looks fine untilSecondis completely back in control, and then it suddenly changes itself toFirst-colors. This should not happen.
I made a few changes to my RootViewController to make it a little better. I removed viewWillAppear: completely, and changed it with this:
class RootViewController:UIViewController{ override func willMove(toParentViewController parent: UIViewController?) { if let last = self.navigationController?.viewControllers.last as? RootViewController{ if last == self && self.navigationController!.viewControllers.count > 1{ if let parent = self.navigationController!.viewControllers[self.navigationController!.viewControllers.count - 2] as? RootViewController{ parent.setNavigationColors() } } } } override func viewWillDisappear(_ animated: Bool) { if let parent = navigationController?.viewControllers.last as? RootViewController{ parent.animateNavigationColors() } } override func viewDidAppear(_ animated: Bool) { self.setNavigationColors() } func animateNavigationColors(){ transitionCoordinator?.animate(alongsideTransition: { [weak self](context) in self?.setNavigationColors() }, completion: nil) } func setNavigationColors(){ //Override in subclasses } } With this updated code, I get this: 
A few observations:
- The transition from
FirsttoSecondis the same - The pop-transition from
SecondtoFirstis now animating correctly, except from the back-arrow, the back-text (and the statusBar, but yeah..). These are instantly changed to black. In the first gif, you could see that the back-arrow and the back-text also transitioned. - The drag-transition from
SecondtoFirstalso has this problem, the back-arrow and back-text are suddenly instantly black when starting. The barTint is fixed so that it doesn't get the wrong color when cancelling the drag.
What am I doing wrong? How am I supposed to do this?
What I want is to transition all elements smoothly. The tint of the back-button, the back-text, the title, the barTint, and the statusBar. Is this not possible?
3 Answers
Answers 1
You can overwrite the push and pop methods of UINavigationController to set the bar color. I've stored the bar color corresponding to a view controller in its navigation item with a custom subclass of UINavigationItem. The following code works for me in iOS 11 for full and for interactive transitions as well:
import UIKit class NavigationItem: UINavigationItem { @IBInspectable public var barTintColor: UIColor? } class NavigationController: UINavigationController, UIGestureRecognizerDelegate { func applyTint(_ navigationItem: UINavigationItem?) { if let item = navigationItem as? NavigationItem { self.navigationBar.barTintColor = item.barTintColor } } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) applyTint(self.topViewController?.navigationItem) self.interactivePopGestureRecognizer?.delegate = self } override func pushViewController(_ viewController: UIViewController, animated: Bool) { applyTint(viewController.navigationItem) super.pushViewController(viewController, animated: animated) } override func popViewController(animated: Bool) -> UIViewController? { let viewController = super.popViewController(animated: animated) applyTint(self.topViewController?.navigationItem) return viewController } override func popToViewController(_ viewController: UIViewController, animated: Bool) -> [UIViewController]? { let result = super.popToViewController(viewController, animated: animated) applyTint(viewController.navigationItem) return result } override func popToRootViewController(animated: Bool) -> [UIViewController]? { let result = super.popToRootViewController(animated: animated) applyTint(self.topViewController?.navigationItem) return result } func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool { return (otherGestureRecognizer is UIScreenEdgePanGestureRecognizer) } } Note: The coordination of the color animation is done by the navigation controller
Answers 2
I updated my previous answer. I did animation effect without using transition coordinator and they are smooth in every case like push/pop/swipe-back.
https://stackoverflow.com/a/40272975/5433235
Also, you can check it on my github project
Hope it helps you :)
Answers 3
See my answer on the same question: How to set navigation bar to transparent in iOS 11
You can set navbar to transparent and animate view below it.

0 comments:
Post a Comment