In iOS13 the status bar background colour is diffe

2020-01-24 07:20发布

问题:

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.

回答1:

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
}



回答2:

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



回答3:

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


回答4:

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


回答5:

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;


回答6:

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
    }

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
    }



回答7:

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 :)



回答8:

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.



回答9:

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.

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



回答10:

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
               } 


回答11:

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


回答12:

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!]
        }
    }


回答13:

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
    }


回答14:

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!