Search code examples
iosswiftuinavigationbarappdelegate

Delegate function in appDelegate doesn't change navbar colors


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!


Solution

  • 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
        }
    }