In iOS13 the status bar background colour is different from the navigation bar in large text mode

Problems demo

Pre-conditions to reproduce the problem:

  1. Xcode 11 beta + iOS 13 (latest version until Jun. 12 2019)
  2. The navigation bar is in Large text mode
  3. Specify the colour of navigation bar.

The status bar will remain in white in a real device, above the green navigation bar.

Solutions I tried:

  1. Revert it back to iOS12 will solve it, but we will encounter iOS13 eventually...
  2. disabling the large text mode will solve it...
  3. hide the status bar will fix it, but it will cause status text overlapping with navigation bar item.

Any ideas? appreciate any help.

76070 次浏览

On iOS 13, navigation bars using large title have a transparent color per Apple human interface guidelines. See more infos here:

In iOS 13 and later, a large title navigation bar doesn’t include a background material or shadow by default. Also, a large title transitions to a standard title as people begin scrolling the content

If the problem is that you'd like to give the navigation bar a color when the large title is showing, use the new UINavigationBarAppearance class.

let app = UINavigationBarAppearance()
app.backgroundColor = .blue
self.navigationController?.navigationBar.scrollEdgeAppearance = app

I've discovered that with storyboards you have to fake the nav bar (only really works with opaque nav bars, assuming your green is opaque). The best way I found was to create a placeholder view (purple) that fits the safe area insets, and then add a fake view behind the nav bar (cyan/blue) that is the height remaining. Works for my project, but yeah it's a bit of a hack. Screenshot from Xcode 11 beta 4 displaying constraints required for status bar hack

Edit: This is mainly for LaunchScreen.storyboard where you can't use a custom view controller class.

No hacks or funkiness required here. The key is defining the desired appearance and setting this value on BOTH the nav bar's standardAppearance AND its scrollEdgeAppearance. I have the following in the init for my base navigation controller subclass for my entire app:

if #available(iOS 13.0, *) {
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithOpaqueBackground()
navBarAppearance.titleTextAttributes = [.foregroundColor: UIColor.white]
navBarAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor.white]
navBarAppearance.backgroundColor = <insert your color here>
navigationBar.standardAppearance = navBarAppearance
navigationBar.scrollEdgeAppearance = navBarAppearance
}

enter image description here

Thanks to Mike and Hans's answer. My case is half transparent status bar and nav bar with alpha 0.5. iOS13 seems complicated. Below is my test result, will work if you want transparent for both.

if #available(iOS 13.0, *) {
let navBarAppearance = UINavigationBarAppearance()
// This only set top status bar as transparent, not the nav bar.
navBarAppearance .configureWithTransparentBackground()
// This set the color for both status bar and nav bar(alpha 1).
navBarAppearance.backgroundColor = UIColor.red.withAlphaComponent(0.5)
navigationController?.navigationBar.standardAppearance = navBarAppearance
navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
// Nav bar need sets to translucent for both nav bar and status bar to be translucent.
navigationController?.navigationBar.isTranslucent = true
// // Need to reset nav bar's color to make it clear to display navBarAppearance's color
navigationController?.navigationBar.backgroundColor = UIColor.clear
}

Universal code

let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithOpaqueBackground()
navBarAppearance.backgroundColor = // your color
navBarAppearance.shadowImage = nil // line
navBarAppearance.shadowColor = nil // line
UINavigationBar.appearance(whenContainedInInstancesOf: [UINavigationController.self]).standardAppearance = navBarAppearance
UINavigationBar.appearance(whenContainedInInstancesOf: [UINavigationController.self]).scrollEdgeAppearance = navBarAppearance

For iOS 13 I was having a problem with the bar's shadow line showing up. Setting the Bars shadow image to nil solved that issue.

Before

func configureNavigation() {
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithOpaqueBackground()
navBarAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor.myColor,
.font: UIFont(name: "MyFont", size: 42)!]
navBarAppearance.backgroundColor = .white
navigationController?.navigationBar.isTranslucent = false
navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
}

Image with shadow

After

func configureNavigation() {
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithOpaqueBackground()
navBarAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor.myColor,
.font: UIFont(name: "MyFont", size: 42)!]
navBarAppearance.backgroundColor = .white
navBarAppearance.shadowColor = nil
navigationController?.navigationBar.isTranslucent = false
navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
}

Image without shadow

Swift 5

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)


let userInterfaceStyle = traitCollection.userInterfaceStyle
modeDetect(userInterfaceStyle: userInterfaceStyle)


}


override func viewDidAppear(_ animated: Bool) {
navigationController?.navigationBar.barStyle = .black
navigationController?.navigationBar.titleTextAttributes = [.foregroundColor: UIColor.white]
}


func modeDetect(userInterfaceStyle: UIUserInterfaceStyle) {
switch userInterfaceStyle {
case .light:
navigationController?.navigationBar.barTintColor = .systemPink
case .dark:
navigationController?.navigationBar.barTintColor = .systemBackground
default:
break
}
}

The following Objective-C code made iOS 13 navigation bar behave like the previous version for me:

    if (@available(iOS 13.0, *)) {
// Setup iOS 13 navigation bar
sharedSelector.navigationBar.scrollEdgeAppearance = sharedSelector.navigationBar.standardAppearance;
} else {
// Fallback on earlier versions
}

Objective C Solutions and iOS 13

UINavigationBarAppearance* navBarAppearance = [self.navigationController.navigationBar standardAppearance];
[navBarAppearance configureWithOpaqueBackground];
navBarAppearance.titleTextAttributes = @{NSForegroundColorAttributeName:TitleColor};
navBarAppearance.largeTitleTextAttributes = @{NSForegroundColorAttributeName: TitleColor};
navBarAppearance.backgroundColor = TopColor;
self.navigationController.navigationBar.standardAppearance = navBarAppearance;
self.navigationController.navigationBar.scrollEdgeAppearance = navBarAppearance;

my navigationBar extension, iOS 13 Swift 5

extension UIViewController {
func configureNavigationBar(largeTitleColor: UIColor, backgoundColor: UIColor, tintColor: UIColor, title: String, preferredLargeTitle: Bool) {
if #available(iOS 13.0, *) {
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithOpaqueBackground()
navBarAppearance.largeTitleTextAttributes = [.foregroundColor: largeTitleColor]
navBarAppearance.titleTextAttributes = [.foregroundColor: largeTitleColor]
navBarAppearance.backgroundColor = backgoundColor


navigationController?.navigationBar.standardAppearance = navBarAppearance
navigationController?.navigationBar.compactAppearance = navBarAppearance
navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance


navigationController?.navigationBar.prefersLargeTitles = preferredLargeTitle
navigationController?.navigationBar.isTranslucent = false
navigationController?.navigationBar.tintColor = tintColor
navigationItem.title = title


} else {
// Fallback on earlier versions
navigationController?.navigationBar.barTintColor = backgoundColor
navigationController?.navigationBar.tintColor = tintColor
navigationController?.navigationBar.isTranslucent = false
navigationItem.title = title
}
}}

How to use:

configureNavigationBar(largeTitleColor: .yourColor, backgoundColor: .yourColor, tintColor: .yourColor, title: "YourTitle", preferredLargeTitle: true)

Set ViewController-based status bar...... to NO in info.plist if you want light Content

If you don't want largeTitles set it to false

Tested on iOS 13, hope this help :)

I had a similar problem when updating one of my apps to be more iOS 13 compatible. As Hans mentioned above, the large titles are transparent by default. If you are a heavy Storyboard user as I am, there's another setting in the side bar that's easy to turn on.

If you click on your nav bar in the story board, it usually defaults to selecting the Navigation Item, and you won't get any customization options. Select the Navigation Bar option above it, and then you are able to choose a custom background color of whatever you want over in the Inspector on the right.

enter image description here

Call this function with a proper argument. This code is working properly.

open func showNavigationBar(large: Bool,
animated: Bool,
isTransparabar: Bool,
titleColor: UIColor,
barBackGroundColor: UIColor,
fontSize: CGFloat) {


navigationController?.navigationBar.barTintColor = barBackGroundColor
navigationController?.navigationBar.backgroundColor = barBackGroundColor
navigationController?.navigationBar.isTranslucent = true
self.navigationController?.setNavigationBarHidden(false, animated: animated)
if large {
self.navigationController?.navigationBar.prefersLargeTitles = true
if #available(iOS 13.0, *) {
let appearance = UINavigationBarAppearance()
appearance.backgroundColor = barBackGroundColor
appearance.titleTextAttributes = [.foregroundColor: titleColor]
appearance.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: titleColor]


navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.compactAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance = appearance
} else {
self.navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: titleColor]
}
} else {
self.navigationController?.navigationBar.prefersLargeTitles = false
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: titleColor!]
}
}

Mike's solution is great.

I offer an alternative approach to change the UINavigationBar color which applies to any iOS version.

We are basically going to exploit the fact that we can set an Image as the UINavigationBar's background.

FIRST

Add an extension to generate a UIImage from any UColor. Note that you can also also write an extension later to generate UIColor from hexadecimals or other formats if you want.

extension UIColor {
func image(_ size: CGSize = CGSize(width: 1, height: 1)) -> UIImage {
return UIGraphicsImageRenderer(size: size).image { rendererContext in
self.setFill()
rendererContext.fill(CGRect(origin: .zero, size: size))
}
}


}

Thanks @neoneye for this great UIColor extension :)

SECOND

Now we get down to business:

private func setupNavigationBarAppearance(navBar: UINavigationBar) {
navBar.isTranslucent = false
let navBarColorImage = UIColor.blue.image()
navBar.setBackgroundImage(navBarColorImage, for: .default)
navBar.tintColor = UIColor.white
}

Because we've setup the opaque colored image as the background, we do not need to check for iOS 13.

I hope this help some folks to customize their NavBars.

Cheers!

If you want to remove the underline below the nav bar

if #available(iOS 13.0, *) {
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithOpaqueBackground()
navBarAppearance.titleTextAttributes = [.foregroundColor: UIColor.white]
navBarAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor.white]
navBarAppearance.backgroundColor = <yourColor>
navBarAppearance.backgroundImage = UIImage()
navBarAppearance.shadowImage = UIImage()
navBarAppearance.shadowColor = .clear
self.navigationController?.navigationBar.standardAppearance = navBarAppearance
self.navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
   

}

Fully workable code:

 let navigationBarAppearace = UINavigationBar.appearance()
navigationBarAppearace.tintColor = .tintColor
navigationBarAppearace.barTintColor = .barTintColor
navigationBarAppearace.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.tintColor]


if #available(iOS 13.0, *) {
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithOpaqueBackground()
navBarAppearance.titleTextAttributes = [.foregroundColor: UIColor.tintColor]
navBarAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor.tintColor]
navBarAppearance.backgroundColor = <insert your color here>
navigationBarAppearace.standardAppearance = navBarAppearance // have a look here ;)
navigationBar.scrollEdgeAppearance = navBarAppearance
}

Good luck all, Peace!

I just turn on translucent in storyboard

like here

@mohmmad alabid's answer would need to be applied to each view controller seperately, if you want a generic objective-c solution put this in your app delegate inside the didFinishLaunchingWithOptions function.

if (@available(iOS 13.0, *)) {
UINavigationBarAppearance* navBarAppearance = [[UINavigationBarAppearance alloc] init];
[navBarAppearance configureWithOpaqueBackground];
navBarAppearance.titleTextAttributes = @{NSForegroundColorAttributeName:NAVBAR_TEXT_COLOR};
navBarAppearance.largeTitleTextAttributes = @{NSForegroundColorAttributeName: NAVBAR_TEXT_COLOR};
navBarAppearance.backgroundColor = NAVBAR_COLOR;
[[UINavigationBar appearance] setStandardAppearance: navBarAppearance];
[[UINavigationBar appearance] setScrollEdgeAppearance: navBarAppearance];
}