162

My iOS app uses the storyboard for the UI and uses a custom tint for the background color of the navigation bar.

I have tested my app on the Xcode 13 beta 5 and the navigation bar is "white" and the text on the navigation bar is not visible.

In the apple developer forum at https://developer.apple.com/forums/thread/682420 it states that "In iOS 15, UIKit has extended the usage of the scrollEdgeAppearance, which by default produces a transparent background, to all navigation bars." To restore the old look, you must adopt the new UINavigationBar appearance APIs

I added the following code (from the link above) to the App Delegate "application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions":

        if #available(iOS 13, *) {
            let navigationController = UINavigationController(navigationBarClass: nil, toolbarClass: nil)
            let navigationBar = navigationController.navigationBar
            let appearance = UINavigationBarAppearance()
            appearance.configureWithOpaqueBackground()
            appearance.backgroundColor = UIColor(red: 0.0/255.0, green: 125/255.0, blue: 0.0/255.0, alpha: 1.0)
            navigationBar.standardAppearance = appearance;
            navigationBar.scrollEdgeAppearance = navigationBar.standardAppearance
            navigationBar.isTranslucent = false
        }

This does not fix the problem. I still have the custom tint set in the storyboard editor for the navigation bar. Do I need to remove the custom tint or am I implementing the appearance API wrong?

G. Steve
  • 2,739
  • 2
  • 11
  • 17
  • You are creating a new instance of UINavigationController in your code snippet. What are you doing with this instance? Just a wild guess: I think you are looking for something like `UINavigationBar.appearance()` but I am not familiar with the new iOS 15 APIs to be honest. – Teetz Sep 09 '21 at 10:23
  • https://developer.apple.com/forums/thread/682420 – Trenton McKinney Oct 08 '21 at 17:57

19 Answers19

162

To use your own colour scheme, use the following:

Swift

// White non-transucent navigatio bar, supports dark appearance
if #available(iOS 15, *) {
    let appearance = UINavigationBarAppearance()
    appearance.configureWithOpaqueBackground()
    UINavigationBar.appearance().standardAppearance = appearance
    UINavigationBar.appearance().scrollEdgeAppearance = appearance
}

Objective-C

if (@available(iOS 15.0, *)) {
    UINavigationBarAppearance *navBarAppearance = [[UINavigationBarAppearance alloc] init];
    navBarAppearance.backgroundColor = [UIColor redColor];
    [navBarAppearance configureWithOpaqueBackground];
    [UINavigationBar appearance].standardAppearance = navBarAppearance;
    [UINavigationBar appearance].scrollEdgeAppearance = navBarAppearance;
}

To get the default translucent behaviour, the way the default was before iOS 15, just set the scrollEdgeAppearance:

Swift

if #available(iOS 15, *) {
    UINavigationBar.appearance().scrollEdgeAppearance = UINavigationBarAppearance()
}

Objective-C

if (@available(iOS 15.0, *)) {
    [UINavigationBar appearance].scrollEdgeAppearance = [[UINavigationBarAppearance alloc] init]; 
}
Boris Y.
  • 4,387
  • 2
  • 32
  • 50
  • 7
    Thank you. Not sure why the other answer has so many upvotes, but this gives the correct "pre-Xcode 13" nav bar behavior. You can also use configureWithDefaultBackground() to retain the old transparency effect if you would like. – Adam Kaump Sep 20 '21 at 21:03
  • I have multiple navigationControllers, some with different colours. I noticed that the colour change isn't happening immediately for me here when I navigate into them, and the colours are only correct on the second time i navigate. How could I correct that? – Jargen89 Oct 01 '21 at 13:26
  • 5
    I figured it out. To apply different colours to multiple navigationControllers, I assign the `standardAppearance` and `scrollEdgeAppearance` of self.navigationController.navigationBar rather than UINavigationBar.appearance(). I do have UINavigationBar.appearance() being modified in my appDelegate, but that is for the general default value I am aiming to set. – Jargen89 Oct 01 '21 at 13:36
  • My header section shows the color which i have set in the app, but failed to show for some screen in iOS 15. Also the menu bar not get closed – sejn Nov 08 '21 at 11:52
  • How can I show the same header color in iOS 15? – sejn Nov 08 '21 at 11:55
  • it would be nice to have a solution to this problem, while still building with Xcode 12.5.1 :/ – nodebase Nov 15 '21 at 22:18
  • 2
    Thank you for the answer. I think it works great but you should edit it and place the line with the new backgroundColor behind "configureWithOpaqueBackground". Else the configure call resets the color to the system defaults – Frank Hartmann Dec 17 '21 at 09:47
  • Thanks much for this tip, I also used this for the [UITabBar appearance] – Pinchus G. Jan 07 '22 at 12:01
  • What should be the fallback code for this functionality? – Sazzad Hissain Khan Jan 11 '22 at 13:53
  • Is it just me or does this have no effect on iPadOS. – Jonny Nov 02 '22 at 10:42
93

There is no need to change anything in the storyboard. Here is the solution that finally worked when added to the App Delegate application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:

//Fix Nav Bar tint issue in iOS 15.0 or later - is transparent w/o code below
if #available(iOS 15, *) {
    let appearance = UINavigationBarAppearance()
    appearance.configureWithOpaqueBackground()
    appearance.titleTextAttributes = [.foregroundColor: UIColor.white]
    appearance.backgroundColor = UIColor(red: 0.0/255.0, green: 125/255.0, blue: 0.0/255.0, alpha: 1.0)
    UINavigationBar.appearance().standardAppearance = appearance
    UINavigationBar.appearance().scrollEdgeAppearance = appearance
}

Note that it was necessary to set the title text attribute to "white" as the title text defaulted to black if this attribute was not specified.

Also note that this should only apply to iOS version 15.0 or later. It does not work for earlier versions as the storyboard navigation bar custom tint is the default behavior.

IluSioN
  • 923
  • 8
  • 20
G. Steve
  • 2,739
  • 2
  • 11
  • 17
  • 6
    I think you should replace `navigationBar.standardAppearance = appearance;` by `UINavigationBar.appearance().standardAppearance = appearance`. – IluSioN Sep 16 '21 at 15:14
  • Yes you are correct - noticed this after posting. – G. Steve Sep 17 '21 at 15:48
  • Updated above to reflect correction by @IluSioN – G. Steve Oct 07 '21 at 17:58
  • where to place this code? – Mahmudur Rahman Oct 19 '21 at 12:31
  • I added in the App Delegate *application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions* – G. Steve Oct 20 '21 at 18:08
  • you can use `UINavigationBar.appearance().tintColor = .red` to set the tint color for any iOS version, the above solution works well for iOS 14 and 15 if used with the mentioned `tintColor` property in this comment – JAHelia Oct 26 '21 at 06:39
  • I had this code, but it was in the wrong place. The navBar would initially be invisible, but pushing and then popping a viewController would 'refresh' the navBar to become visible again. But by putting it in didFinishLaunchingWithOptions, it's visible from the get go. Thanks! – iRon111 Jun 04 '22 at 00:28
  • how do you get the status bar tint color to be white? I can only see it as black. – cspam Jun 13 '22 at 23:52
  • I made it a custom color above, but you should be able to use: appearance.backgroundColor = UIColor(white: 255.0/255.0, alpha: 1.0) – G. Steve Jun 15 '22 at 00:24
44

If anyone needs the Objective C version of G. Steve's answer

if (@available(iOS 15, *)){
        UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init];
        [appearance configureWithOpaqueBackground];
        appearance.titleTextAttributes = @{NSForegroundColorAttributeName : UIColor.whiteColor};
        appearance.backgroundColor = [UIColor colorWithRed:0.0/255.0 green:125/255.0 blue:0.0/255.0 alpha:1.0];
        [UINavigationBar appearance].standardAppearance = appearance;
        [UINavigationBar appearance].scrollEdgeAppearance = appearance;
    }
IluSioN
  • 923
  • 8
  • 20
31

It get sorted for me in interface builder (xcode 13 - tested for iOS 13 and above) and did not need to check for iOS 15 availability (i.e. @available)

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

enter image description here

  1. Choose similar settings for both appearances

enter image description here

enter image description here

Good luck

Atka
  • 547
  • 4
  • 7
  • This answer is very useful. But would you share how can I change title colour and title font in Interface builder. I tried but I didn't get success. Thank you – iPhone Oct 19 '21 at 12:58
  • You need to set the `titleTextAttributes` property with your preferences. – Kumar C Nov 09 '21 at 05:22
  • 1
    Note that this only works when your app targets a minimum version of iOS 13.0 or higher. When set to support older iOS versions, this will result in error `Main.storyboard: error: Class Unavailable: Navigation Bar Appearances before iOS 13.0`. – Martijn Jul 21 '22 at 11:22
17

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

enter image description here

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

enter image description here

wlixcc
  • 1,132
  • 10
  • 14
11

This code can be put anywhere, not just in the App Delegate to fix the issue on iOS15:

if (@available(iOS 15, *)){
        UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init];
        [appearance configureWithOpaqueBackground];
        appearance.titleTextAttributes = @{NSForegroundColorAttributeName : UIColor.blackColor};
        appearance.backgroundColor = [UIColor colorWithRed:0.0/255.0 green:125/255.0 blue:0.0/255.0 alpha:1.0];
        self.navigationController.navigationBar.standardAppearance = appearance;
        self.navigationController.navigationBar.scrollEdgeAppearance = appearance;
    }
  • Code only answers are not considered good practice. Please consider [Explaining how this answers the question](https://meta.stackoverflow.com/questions/300837/what-comment-should-i-add-to-code-only-answers) – DevWithZachary Oct 08 '21 at 13:07
8

Xcode 13+

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.

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.backgroundColor = <your tint color>
    navigationBar.standardAppearance = appearance
    navigationBar.scrollEdgeAppearance = appearance

You can also use the appearance proxy with the code above, but substituting navigationBar.appearance().scrollEdgeAppearance = appearance for the last line.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
YodagamaHeshan
  • 4,996
  • 2
  • 26
  • 36
5

I've created this extension for supporting iOS 15 and iOS 12 for changing navigation bar background (tint) and title colors only in needed places, not over all application.

extension UINavigationBar {
  func update(backroundColor: UIColor? = nil, titleColor: UIColor? = nil) {
    if #available(iOS 15, *) {
      let appearance = UINavigationBarAppearance()
      appearance.configureWithOpaqueBackground()
      if let backroundColor = backroundColor {
        appearance.backgroundColor = backroundColor
      }
      if let titleColor = titleColor {
        appearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: titleColor]
      }
      standardAppearance = appearance
      scrollEdgeAppearance = appearance
    } else {
      barStyle = .blackTranslucent
      if let backroundColor = backroundColor {
        barTintColor = backroundColor
      }
      if let titleColor = titleColor {
        titleTextAttributes = [NSAttributedString.Key.foregroundColor: titleColor]
      }
    }
  }
}

and use it in needed places (in my case it's UI configuration of UIViewController) like this

  func configureNavigationController() {
    navigationController?.navigationBar.update(backroundColor: .blue, titleColor: .white)
  }
mike.stalker
  • 442
  • 1
  • 8
  • 13
3

I tried various ways but below code worked like Magic for restoring previous version.

    if #available(iOS 15, *) {
        let appearance = UINavigationBarAppearance()
        appearance.configureWithOpaqueBackground()
        appearance.backgroundColor = .white
        UINavigationBar.appearance().standardAppearance = appearance
        UINavigationBar.appearance().scrollEdgeAppearance = appearance
    }
Kudos
  • 1,224
  • 9
  • 21
2

My implementation of navigation bar configuration as opaque and as translucent for iOS 15 and older versions:

extension UINavigationBar {
static let defaultBackgroundColor = UIColor.red
static let defaultTintColor = UIColor.white

func setOpaque() {
    if #available(iOS 15, *) {
        let appearance = UINavigationBarAppearance()
        appearance.configureWithOpaqueBackground()
        appearance.backgroundColor = UINavigationBar.defaultBackgroundColor
        appearance.titleTextAttributes = [.foregroundColor: UINavigationBar.defaultTintColor]
        
        UINavigationBar.appearance().standardAppearance = appearance
        UINavigationBar.appearance().scrollEdgeAppearance = appearance
    } else {
        setBackgroundImage(UIImage(), for: UIBarPosition.any, barMetrics: UIBarMetrics.defaultPrompt)
        shadowImage = UIImage()
        barTintColor = UINavigationBar.defaultBackgroundColor
        titleTextAttributes = [.foregroundColor: UINavigationBar.defaultTintColor]
    }
    isTranslucent = false
    tintColor = UINavigationBar.defaultTintColor
}

func setTranslucent(tintColor: UIColor, titleColor: UIColor) {
    if #available(iOS 15, *) {
        let appearance = UINavigationBarAppearance()
        appearance.configureWithTransparentBackground()
        appearance.titleTextAttributes = [.foregroundColor: titleColor]
        standardAppearance = appearance
        scrollEdgeAppearance = appearance
    } else {
        titleTextAttributes = [.foregroundColor: titleColor]
        setBackgroundImage(UIImage(), for: UIBarMetrics.default)
        shadowImage = UIImage()
    }
    isTranslucent = true
    self.tintColor = tintColor
}

}

2

In AppDelegate.swift

window?.backgroundColor = .white

worked in my case

pableiros
  • 14,932
  • 12
  • 99
  • 105
Keval Shah
  • 51
  • 8
2

Anybody looking for an objective-c solution, please try out the below code:

if (@available(iOS 15.0, *)) {
        UINavigationBarAppearance *navBarAppearance = [[UINavigationBarAppearance alloc] init];
        [navBarAppearance configureWithOpaqueBackground];
        navBarAppearance.backgroundColor = YOUR_COLOR;
        [navBarAppearance setTitleTextAttributes:
                @{NSForegroundColorAttributeName:[UIColor whiteColor]}];

        self.navigationController.navigationBar.standardAppearance = navBarAppearance;
        self.navigationController.navigationBar.scrollEdgeAppearance = navBarAppearance;
    }
Mahendra Liya
  • 12,912
  • 14
  • 88
  • 114
  • This has already been mentioned in other answers such as [this one](https://stackoverflow.com/a/69493819/2227743) or [this one](https://stackoverflow.com/a/69469648/2227743). *When answering older questions that already have answers, please make sure you provide either a novel solution or a significantly better explanation than existing answers.* – Eric Aya Oct 27 '21 at 14:01
  • @EricAya Hey, I did try to look before sharing the answer, however, I couldn't find an **Objective-c** solution. – Mahendra Liya Oct 30 '21 at 07:41
  • Both answers I've linked to are in Objective-C. Also maybe you didn't notice but the question is tagged Swift. – Eric Aya Oct 30 '21 at 09:04
1

Objective c code : implement this in your viewDidLoad function


if (@available(iOS 15, *)){
    UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init];
    [appearance configureWithOpaqueBackground];
    appearance.titleTextAttributes = @{NSForegroundColorAttributeName : UIColor.blackColor};
    appearance.backgroundColor = [UIColor colorWithRed:0.0/255.0 green:125/255.0 blue:0.0/255.0 alpha:1.0];
    self.navigationController.navigationBar.standardAppearance = appearance;
    self.navigationController.navigationBar.scrollEdgeAppearance = appearance;
}
1

If we need to change the background color and selected and unselected item color, only this code worked in case of me

i have used this to change item appearance tabBarAppearance.stackedLayoutAppearance = tabBarItemAppearance

  if #available(iOS 15.0, *) {
        
        let tabBarAppearance = UITabBarAppearance()
        let tabBarItemAppearance = UITabBarItemAppearance()
        
        tabBarAppearance.backgroundColor = .white
        
        tabBarItemAppearance.normal.titleTextAttributes = [NSAttributedString.Key.foregroundColor: Constants.Color.appDefaultBlue]
        tabBarItemAppearance.selected.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.black]
        
        tabBarAppearance.stackedLayoutAppearance = tabBarItemAppearance
        tabBar.standardAppearance = tabBarAppearance
        tabBar.scrollEdgeAppearance = tabBarAppearance
        
    }

Make sure we use this code in TabBar class, to get the desired results, it might not work if we use it in the AppDelegate to set the appearance.

Stefano Sansone
  • 2,377
  • 7
  • 20
  • 39
1

To do this with storyboard only, to build over @Atka's answer,

you can set customise title text attributes by opting for "Custom" title attributes

Scroll edge title attributes

Shardul
  • 27,760
  • 6
  • 37
  • 35
1

Here is a version if you want to set custom back button without title and transparent navigation bar

let backImg: UIImage = #imageLiteral(resourceName: "back")

if #available(iOS 15, *) {
    let appearance = UINavigationBarAppearance()
    appearance.configureWithOpaqueBackground()
    appearance.titleTextAttributes = [.foregroundColor: UIColor.black]
    appearance.setBackIndicatorImage(backImg, transitionMaskImage: backImg)
    appearance.backButtonAppearance.normal.titlePositionAdjustment =
    UIOffset(horizontal: -1000.0, vertical: 0)
    UINavigationBar.appearance().standardAppearance = appearance
    UINavigationBar.appearance().scrollEdgeAppearance = appearance
}
0

This code can be put anywhere, not just in the App Delegate to fix the issue on iOS15:

                if #available(iOS 15, *) {
                
                let appearance = UINavigationBarAppearance()
                appearance.configureWithOpaqueBackground()
                appearance.backgroundColor = <desired UIColor>
                navigationBar.standardAppearance = appearance;
                navigationBar.scrollEdgeAppearance = navigationBar.standardAppearance
                }
Charlie S
  • 4,366
  • 6
  • 59
  • 97
0

Make like this:

let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = .red
appearance.titleTextAttributes = [.font: 
UIFont.boldSystemFont(ofSize: 20.0),
                              .foregroundColor: UIColor.white]

// Customizing our navigation bar
navigationController?.navigationBar.tintColor = .white
navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance = appearance

I wrote a new article talking about it.

https://medium.com/@eduardosanti/uinavigationbar-is-black-on-ios-15-44e7852ea6f7

Eduardo Santi
  • 425
  • 1
  • 4
  • 13
0

I have edited the code shared by @Charlie Seligman as it did not work for me as I had a scroll view in one of my screen. The below code works even if you have a scroll view and a navigation bar.

if #available(iOS 15, *) {
            let appearance = UINavigationBarAppearance()
            appearance.configureWithOpaqueBackground()
            appearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
            appearance.backgroundColor = UIColor(red: 0.89, green: 0.06, blue: 0.00, alpha: 1.00)
            UINavigationBar.appearance().standardAppearance = appearance
            UINavigationBar.appearance().scrollEdgeAppearance = appearance
        }
Dharman
  • 30,962
  • 25
  • 85
  • 135
Developer
  • 31
  • 1
  • 8