Xcode 11.4. Navigation's Title Color gone BLACK from storyboard

I recently updated my Xcode to 11.4. When I run the app on the device, i've noticed that all my navigations item's titles gone fully black when being set from storyboard. enter image description here

You can't change neither from code, the following line of code doesn't work anymore

self.navigationController?.navigationBar.titleTextAttributes = [.foregroundColor: UIColor.white]

I only make it work using some iOS 13 stuffs UINavigationBarAppearance

@available(iOS 13.0, *)
    private func setupNavigationBar() {
        let app = UINavigationBarAppearance()
        app.titleTextAttributes = [.foregroundColor: UIColor.white]
        app.backgroundColor = Constants.Color.barColor
        self.navigationController?.navigationBar.compactAppearance = app
        self.navigationController?.navigationBar.standardAppearance = app
        self.navigationController?.navigationBar.scrollEdgeAppearance = app

        self.navigationController?.navigationBar.titleTextAttributes = [.foregroundColor: UIColor.white]
    }

Can somebody explain me why??? This is a crucial bug, or some new hidden feature?


Solution 1:

This fixed it for me, using UINavigationBarAppearance instead, from: Customizing Your App’s Navigation Bar

if #available(iOS 13.0, *) {
    let appearance = UINavigationBarAppearance()
    appearance.configureWithOpaqueBackground()
    appearance.backgroundColor = UIColor.black
    appearance.titleTextAttributes = [.foregroundColor: UIColor.white] // With a red background, make the title more readable.
    self.navigationBar.standardAppearance = appearance
    self.navigationBar.scrollEdgeAppearance = appearance
    self.navigationBar.compactAppearance = appearance // For iPhone small navigation bar in landscape.
} else {
    self.navigationBar.barTintColor = UIColor.black
    self.navigationBar.tintColor = UIColor.white
    self.navigationBar.titleTextAttributes = [.foregroundColor: UIColor.white]
}

Note: I subclassed UINavigationController, and this was called from the override of viewWillAppear.

...or for AppDelegate, app-wide:

if #available(iOS 13.0, *) {
    let appearance = UINavigationBarAppearance()
    appearance.configureWithOpaqueBackground()
    appearance.backgroundColor = UIColor.black
    appearance.titleTextAttributes = [
        NSAttributedStringKey.foregroundColor: UIColor.white
    ]

    let buttonAppearance = UIBarButtonItemAppearance()
    buttonAppearance.normal.titleTextAttributes = [.foregroundColor: UIColor.white]
    appearance.buttonAppearance = buttonAppearance

    UINavigationBar.appearance().standardAppearance = appearance
    UINavigationBar.appearance().scrollEdgeAppearance = appearance
    UINavigationBar.appearance().compactAppearance = appearance

    UIBarButtonItem.appearance().tintColor = UIColor.white
} else {
    UINavigationBar.appearance().barTintColor = UIColor.black
    UINavigationBar.appearance().titleTextAttributes = [
        NSAttributedStringKey.foregroundColor: UIColor.white
    ]
    UINavigationBar.appearance().tintColor = UIColor.white

    UIBarButtonItem.appearance().tintColor = UIColor.white
}

...for AppDelegate, app-wide, in Objective-C:

if (@available(iOS 13, *)) {
    UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init];
    [appearance configureWithOpaqueBackground];
    appearance.backgroundColor = UIColor.whiteColor;
    appearance.titleTextAttributes = titleAttributes;

    UIBarButtonItemAppearance *buttonAppearance = [[UIBarButtonItemAppearance alloc] init];
    buttonAppearance.normal.titleTextAttributes = barButtonItemAttributes;
    appearance.buttonAppearance = buttonAppearance;

    UINavigationBar.appearance.standardAppearance = appearance;
    UINavigationBar.appearance.scrollEdgeAppearance = appearance;
    UINavigationBar.appearance.compactAppearance = appearance;

    [[UINavigationBar appearance] setTintColor:UIColor.blackColor];
} else {
    [[UINavigationBar appearance] setBarTintColor:UIColor.whiteColor];
    [[UINavigationBar appearance] setTintColor:UIColor.blackColor];
    [[UINavigationBar appearance] setTranslucent:false];
    [[UINavigationBar appearance] setTitleTextAttributes: titleAttributes];
    [[UIBarButtonItem appearance] setTitleTextAttributes:barButtonItemAttributes forState:UIControlStateNormal];
}

Solution 2:

On the storyboard, for your Navigation Controller change the "Bar Tint" to its "Default" value, then on your code you can change it as you normally would.

Solution 3:

Apple finally fixed it in version 11.4.1

https://developer.apple.com/documentation/xcode_release_notes/xcode_11_4_1_release_notes

Solution 4:

Not sure if it's a bug or not.

The way we fixed it is by setting the "Status Bar Style" to either dark or light content in project setting. This will force the Status Bar text color a certain way rather than being determined based on the devices being in Light or Dark mode.

In addition, you need to set the value "View controller-based status bar appearance" to "NO" in your Info.plist. without that value the "Status Bar style" will be overridden.

Next create a custom navigation controller and implement it in your storyboards.

class CustomNavigationController: UINavigationController {

 override func viewDidLoad() {
    super.viewDidLoad()
    setNavBar()
 }

 func setNavBar() {
    if #available(iOS 13.0, *) {
        let appearance = UINavigationBarAppearance()
        appearance.configureWithOpaqueBackground()
        appearance.backgroundColor = UIColor.blue
        appearance.titleTextAttributes = [.foregroundColor: UIColor.yellow]
        self.navigationBar.standardAppearance = appearance
        self.navigationBar.scrollEdgeAppearance = appearance
        self.navigationBar.compactAppearance = appearance
    } else {
        self.navigationBar.barTintColor = UIColor.blue
        self.navigationBar.titleTextAttributes = [.foregroundColor: UIColor.yellow]
    }
  }
}

*Colors are set so you can see them clearly working.

I found it was better to set the code in ViewDidLoad rather than ViewDidAppear because my colors were not being set on the initial load, only after navigating back and reloading.

I also found that this issue might be tied to the "Bar Tint" of a NavBar. when we were first trying to resolve it, we set the "Bar Tint" to default and that seemed resolve the error too. However, it made it so we couldn't get the NavBar background color what we wanted. So in my storyboards I made sure to set this value to default just for good measure.

Hope it helps

Solution 5:

no need for the workaround.it is a bug in Xcode Interface Builder. Apple release Update for Xcode 11.4.1

from Apple developer release notes

Interface Builder

Fixed an issue that caused some UINavigationBar appearance properties set in storyboard and XIB documents to be ignored when building with Xcode 11.4. (60883063) (FB7639654)

https://developer.apple.com/documentation/xcode_release_notes/xcode_11_4_1_release_notes