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
First
toSecond
looks 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
Second
toFirst
is completely wrong. It keeps the colors fromSecond
until the transition is completely done. - The drag-transition from
Second
toFirst
looks 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
Second
toFirst
but cancelled mid-drag and returning toSecond
is completely screwed up. It looks fine untilSecond
is 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
First
toSecond
is the same - The pop-transition from
Second
toFirst
is 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
Second
toFirst
also 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