Xcode 11.3.1, iOS 13
I'm trying to change the color of the navigationBar on all View Controllers if a certain condition exists in the app. Seemed logical to use the same code that sets the global color initially, using a delegate function in AppDelegate.
Here's my code:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var myColor : UIColor?
let themeColorUS = UIColor(red: 0.991, green: 0.621, blue: 0.022, alpha: 1.00)
let themeColorCanada = UIColor(red: 0.001, green: 0.686, blue: 0.000, alpha: 1.00)
let themeColorGeneral = UIColor(red: 0.000, green: 0.954, blue: 0.969, alpha: 1.00)
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UserDefaults.standard.setValue(false, forKey: "_UIConstraintBasedLayoutLogUnsatisfiable")
setBarColors(issuingFlag: "General")
return true
}
...
func setBarColors(issuingFlag:String) {
if issuingFlag == "US" {
myColor = themeColorUS
}else if issuingFlag == "Canada"{
myColor = themeColorCanada
}else{
myColor = .magenta
}
print("issuingFlag == \(issuingFlag)")
if #available(iOS 13.0, *) {
let appearance = UINavigationBarAppearance()
appearance.backgroundColor = myColor
appearance.titleTextAttributes = [.foregroundColor: UIColor.black]
appearance.largeTitleTextAttributes = [.foregroundColor: UIColor.black]
UINavigationBar.appearance().tintColor = .black
UINavigationBar.appearance().standardAppearance = appearance
UINavigationBar.appearance().compactAppearance = appearance
UINavigationBar.appearance().scrollEdgeAppearance = appearance
} else {
UINavigationBar.appearance().tintColor = .black
UINavigationBar.appearance().barTintColor = myColor
UINavigationBar.appearance().isTranslucent = false
}
}
In addition to the initial call to setBarColors()
when the app opens (which works fine) I also call it from a viewController within the app like so, which does nothing to the navBars, even though the function is receiving the appropriate data in its parameter:
if detailFlag.issuedBy == "Canada"{
appDelegate.setBarColors(issuingFlag: "Canada")
}else if detailFlag.issuedBy == "US"{
appDelegate.setBarColors(issuingFlag: "US")
}
Can someone help me out as to why the function's not switching the navBar
colors?
TIA!
I'd suggest, since you want to dynamically change the NavigationBar theme (like background color) based on a Flag model value, to not go for the AppDelegate way, as that will do it once for you, and it is thought more as a global way to set the NavigationBar style before any view is actually created.
There are a few ways you can apply that, like through extension ViewController
, inheritance with base class
.. and as well different ways you can get/set the flag values to change the navigation colors, like through userdefaults
, variables
... I'll show an example just to get you going:
import UIKit
class ViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
currentFlag = "Canada"
}
}
class BaseViewController: UIViewController {
var currentFlag: String = "General" {
didSet {
setNavBarColor()
}
}
private let themeColorUS = UIColor(red: 0.991, green: 0.621, blue: 0.022, alpha: 1.00)
private let themeColorCanada = UIColor(red: 0.001, green: 0.686, blue: 0.000, alpha: 1.00)
private let themeColorGeneral = UIColor(red: 0.000, green: 0.954, blue: 0.969, alpha: 1.00)
override func viewDidLoad() {
super.viewDidLoad()
setNavBarColor()
}
private func setNavBarColor() {
navigationController?.navigationBar.barTintColor = getBarColor(for: currentFlag)
}
private func getBarColor(for flag: String) -> UIColor {
if flag == "US" {
return themeColorUS
} else if flag == "Canada" {
return themeColorCanada
}
return themeColorGeneral
}
}
And that means, we removed the global way of setting its style from AppDelegate
so my didFinishLaunchingWithOptions
looks like:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
Running the following code (with flag set to Canada for my ViewController), and having ViewController in storyboard with a root viewcontroller of UINavigationController like this:
Would make the app look like:
Refactoring for improvements
Something you can do in addition, just to make it easier to manage the code and flags and colors, is to organise them in a structure, I choosed an enum as an example but you can do it other ways as well, just to give you a sample you could have done it this way:
import UIKit
enum Flag {
case us
case canada
case general
static let `default` = Flag.general
init(rawValue: String) {
switch rawValue {
case "US":
self = .us
case "Canada":
self = .canada
case "General":
self = .general
default:
self = .default
}
}
var themeColor: UIColor {
switch self {
case .us:
return UIColor(red: 0.001, green: 0.686, blue: 0.000, alpha: 1.00)
case .canada:
return UIColor(red: 0.001, green: 0.686, blue: 0.000, alpha: 1.00)
case .general:
return UIColor(red: 0.000, green: 0.954, blue: 0.969, alpha: 1.00)
}
}
}
class ViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
currentFlag = .canada
}
}
class BaseViewController: UIViewController {
var currentFlag: Flag = .default {
didSet {
setNavBarColor()
}
}
override func viewDidLoad() {
super.viewDidLoad()
setNavBarColor()
}
private func setNavBarColor() {
navigationController?.navigationBar.barTintColor = currentFlag.themeColor
}
}