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 <
in 10 iOS it works imperfectly :(
Subclass your navigation controller to use statusbarstyle of visible view controller:
class MyNavigationController: UINavigationController {
override var preferredStatusBarStyle: UIStatusBarStyle {
return visibleViewController!.preferredStatusBarStyle
}
}
Override preferredStatusBarStyle in Root controller and add function to set styles before pop animation:
private var _preferredStyle = UIStatusBarStyle.default;
override var preferredStatusBarStyle: UIStatusBarStyle {
get {
return _preferredStyle
}
set {
_preferredStyle = newValue
self.setNeedsStatusBarAppearanceUpdate()
}
}
func animateNavigationColors(){
self.setBeforePopNavigationColors()
transitionCoordinator?.animate(alongsideTransition: { [weak self](context) in
self?.setNavigationColors()
}, completion: nil)
}
func setBeforePopNavigationColors() {
//Override in subclasses
}
In first controller:
override func setBeforePopNavigationColors() {
navigationController?.navigationBar.tintColor = UIColor.white
navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
self.preferredStatusBarStyle = UIStatusBarStyle.lightContent
}
override func setNavigationColors(){
navigationController?.navigationBar.barTintColor = UIColor.white
navigationController?.navigationBar.tintColor = UIColor.black
navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.black]
navigationController?.navigationBar.barStyle = UIBarStyle.default
self.preferredStatusBarStyle = UIStatusBarStyle.default
}
In second:
override func setNavigationColors(){
navigationController?.navigationBar.barTintColor = UIColor.black
navigationController?.navigationBar.tintColor = UIColor.white
navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
navigationController?.navigationBar.barStyle = UIBarStyle.black
self.preferredStatusBarStyle = UIStatusBarStyle.lightContent
}
Example project: https://github.com/josshad/TestNavBarTransition
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