barTintColor not working in iOS 15

Hi,

When I run the following code in application(_ :didFinishLaunchingWithOptions) in iOS 15, the bar color turns transparent (thus, showing the black background underneath), while the same code works fine in iOS 14.5:

UINavigationBar.appearance().isTranslucent = false
UINavigationBar.appearance().barTintColor = .red

Here's the screenshots of Simulators running iOS 14.5 and iOS 15:

I'm using Xcode 13 on macOS Big Sur 11.4.

Thanks!

Answered by in 678641022

In iOS 15, UIKit has extended the usage of the scrollEdgeAppearance, which by default produces a transparent background, to all navigation bars. The background is controlled by when your scroll view scrolls content behind the navigation bar. Your screenshots indicate that you are scrolled to the top, and so the navigation bar has selected its scrollEdgeAppearance over the standardAppearance that it would use when scrolled, and on previous versions of iOS.

To restore the old look, you must adopt the new UINavigationBar appearance APIs, UINavigationBarAppearance. Remove your existing customizations and do something like this:

let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = <your tint color>
navigationBar.standardAppearance = appearance;
navigationBar.scrollEdgeAppearance = navigationBar.standardAppearance

In the general case, it is the last line navigationBar.scrollEdgeAppearance = navigationBar.standardAppearance, which resolves the issue by having the UINavigationBar use the same appearance for both its standard and edge states. Also note that this will cause the scroll view to underlap the navigation bar – we recommend against setting UINavigationBar.isTranslucent = true.

You can also use the appearance proxy with the code above, but substituting navigationBar.appearance().scrollEdgeAppearance = appearance for the last line (as you are constructing your own appearance object – the idea is to just make sure both scrollEdge and standard appearances are the same).

Accepted Answer

In iOS 15, UIKit has extended the usage of the scrollEdgeAppearance, which by default produces a transparent background, to all navigation bars. The background is controlled by when your scroll view scrolls content behind the navigation bar. Your screenshots indicate that you are scrolled to the top, and so the navigation bar has selected its scrollEdgeAppearance over the standardAppearance that it would use when scrolled, and on previous versions of iOS.

To restore the old look, you must adopt the new UINavigationBar appearance APIs, UINavigationBarAppearance. Remove your existing customizations and do something like this:

let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = <your tint color>
navigationBar.standardAppearance = appearance;
navigationBar.scrollEdgeAppearance = navigationBar.standardAppearance

In the general case, it is the last line navigationBar.scrollEdgeAppearance = navigationBar.standardAppearance, which resolves the issue by having the UINavigationBar use the same appearance for both its standard and edge states. Also note that this will cause the scroll view to underlap the navigation bar – we recommend against setting UINavigationBar.isTranslucent = true.

You can also use the appearance proxy with the code above, but substituting navigationBar.appearance().scrollEdgeAppearance = appearance for the last line (as you are constructing your own appearance object – the idea is to just make sure both scrollEdge and standard appearances are the same).

66

It worked like a charm! Thank you!

I'm seeing this issue too. I'm surprised that the default behavior is this and that it requires configuration now? I also had to set it for all appearance types to make it look better, but there are still cases where the appearance is strange especially in the transitions between view controllers.

deleted

I have the same problem. The code has to be inserted in each controller and the transitions between the controllers is buggy (Xcode Beta 2, iOS 15 Beta 2).

My problem is the missing top line in the TabBar. I can solve it only by adding a frame. But the frame would have to be visible only at the top. Does anyone have any ideas? Here is my current solution:

tabBarController?.tabBar.tintColor = "myTintColor"
tabBarController?.tabBar.clipsToBounds = true
tabBarController?.tabBar.layer.borderWidth = 0.5
tabBarController?.tabBar.layer.borderColor = UIColor.lightGray.cgColor

thanks!

*** Update iOS15 Beta 3 / Xcode Beta 3 ***

Since beta 3 the problem seems to be solved in the main controller. But when I switch to a subcontroller, the separator line disappears. It no longer appears when I switch back to the main controller. This is definitely a bug!

We faced a similar problem and are considering resolving it with making sure that we always set the background of the view of our view controllers to something other than clear such as systembackgroundcolor, and ensuring that all of our view controllers use UIRectEdgeAll. Previously some view controllers and especially WKWebViews had a clear background and UIRectEdgeNone.

I'd be curious to hear if this helps for anyone else. I like this approach more as it feels less invasive than changing the appearance of the navigation bar.

This Navigation Bar Transparency problem began when Apple made Dark Mode. I told them about it and Apple fixed it but they have broken the Nav Bar again in iOS 15.

The issue persists on iOS 15 Beta 8. Tried on both light/dark theme.

This doesn't seem to work at all with the iOS 15 RC.

VC1 shows VC2. VC1 and VC2 are fine. Their navigation bars are the correct colour.

VC2 presents an image picker (of your library photos). Nothing I do seems to make the image picker use the right colours.

I've tried to use Rincewind's code to style the picker, but it just shows as light grey, with the nav bar buttons in white. I've implemented navigationController:willShowViewController:animated, but whatever I put in there is completely ignored.

It does seem that Apple decide to change the fundamental behaviour of something, and just assume that developers will change their apps to cope with it How about you give us a heads-up before you break our apps?

I've changed .barTintColor to UINavigationBarAppearance backgroundColor, set to standardAppearance and scrollEdgeAppearance and it works. But if present new ViewController modally with modalPresentationStyle == .fullscreen and dismiss, navigationBar's background color on the first one changed to default.

In my case, when I update to xcode13 and iOS15。 I have found that navigationBar and tabBar turns transparent。 My viewController is embed in UINavigationController

After a series of tests, I found the Settings the backgroundColor of navigationController is best Way to fix this

navigationController?.view.backgroundColor = .yourColor

Once the color is set, everything is fine

Hi, little bit late to the game, but i just got this in my app after compiling with xcode 13.

I followed the advice from @Rincewind and got it working, with one exception: When you change the appearances, the navigation text, is then black, not white. The back arrow is still white though.

I managed to sort this out by doing the "largeTextAttributes"-dance : coloredAppearance.titleTextAttributes = [.foregroundColor: UIColor.white] coloredAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor.white]

However, i am surprised that this is not mentioned by anyone in this thread. Howcome i am the only one having this color issue? Am i missing something?

Pointers appreciated.

@cblaze22 asked for an Objective-C version. Here:

UINavigationBarAppearance *navBarAppearance = [[UINavigationBarAppearance alloc] init];
[navBarAppearance configureWithOpaqueBackground];
navBarAppearance.backgroundColor = [UIColor blueColor];
[navBarAppearance setTitleTextAttributes:
        @{NSForegroundColorAttributeName:[UIColor whiteColor]}];

navBar.standardAppearance = navBarAppearance;
navBar.scrollEdgeAppearance = navBarAppearance;
[navBarAppearance release];`
```

After going through all the answers so far and trial and error in interface builder the problem is sorted for me after the following steps: (xcode 13 , iOS 13)

  1. choose standard and scroll edge appearances for the navigation bar.

  1. set both appearances to be the same.

Good luck

In case it helps someone, placing @Rincewind's code snippet in applicationDidBecomeActive restored the pre-iOS15 look of the navigation bar and ensured the navigation controller background view color updated when the user changed between light and dark mode while the app was running.

      let appearance = UINavigationBarAppearance()
      appearance.configureWithOpaqueBackground()
      appearance.backgroundColor = color  // eg .black or .white
      nc.navigationBar.standardAppearance = appearance;
      nc.navigationBar.scrollEdgeAppearance = nc.navigationBar.standardAppearance

In case someone is having issues with navigation title font and color, add the following code to your extension


     if let attributes = self.navigationController?.navigationBar.largeTitleTextAttributes {
        barAppearance.largeTitleTextAttributes = attributes
      }
       
      if let attributes = self.navigationController?.navigationBar.titleTextAttributes {
        barAppearance.titleTextAttributes = attributes
      }

hope it helps

Mathias_, you are not alone, I have the same issue, navigation text is black not white.

I'm having that problem too, but even largeTitleTextAttributes doesn't do it for me. Does anyone got a solution?

found this, that did it for me: https://sarunw.com/posts/uinavigationbar-changes-in-ios13/

basically, set everything in the specific appearance instead in the navigationbar / navigationcontroller

In order to change it everywhere put this code into didFinishLaunchingWithOptions:

if (@available(iOS 15.0, *)) {
    UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init];
    [appearance setBackgroundColor:[UIColor grayColor]];
    [[UINavigationBar appearance] setScrollEdgeAppearance:appearance];
    [[UINavigationBar appearance] setStandardAppearance:appearance];
    [[UINavigationBar appearance] setTintColor:[UIColor grayColor]];
  }

For SwiftUI I found the best thing to do is use the UINavigationBarAppearance() method, then add the .id() to the NavigationView. This will automatically redraw the component when the color changes. Now you can have reactive color changes based on a state engine.

let custom = UINavigationBarAppearance()
custom.configureWithOpaqueBackground()
custom.backgroundColor = color
UINavigationBar.appearance().standardAppearance = custom
UINavigationBar.appearance().scrollEdgeAppearance = custom
UINavigationBar.appearance().compactAppearance = custom
UINavigationBar.appearance().compactScrollEdgeAppearance = custom

Then in the SwiftUI

NavigationView {
  content
}
  .id(color.description)

On iOS 15.2 experiencing issues with both nav bar and tab bar colors messed up. But only when build with Xcode 13. App in prod build with Xcode 12 is looking fine on iOS 15.2

Anyhow, checked out the suggestion from the link T_T_T posted and added a snippet to AppDelegate and it resolved it at least for the nav bar. Thanks for sharing T_T_T!

I was able to solve this using the attributes inspector as follows:

Select the navigation bar. At the top of the attributes inspector, check the box next to Scroll Edge. Under 'Scroll Edge Bar Button Appearances', find the bar tint menu and select the desired color.

barTintColor not working in iOS 15
 
 
Q