Use new UINavigationBarAppearance() API to set navigationBar color on a single ViewController?

206 Views Asked by At

I need to setUp a NavigationBar color for a single ViewController. What Im currently doing is setting up the nav color on viewDidLoad() and resetting it to .clear (so it uses whatever color is set on the newly pushed VC) on viewWillDissappear. While this sorta works, it is not fast enough as the .clear color is not applied until the pushing animation is over, resulting in like half a second of the navigationBar color being visible until it is finally resetted to .clear.

Current code looks like this:

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        navigationController?.navigationBar.setNavBarColor(color: .red)
}

override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        navigationController?.navigationBar.setNavBarColor(color: .clear)
}


func setNavBarColor(color: UIColor) {
    let appearance: UINavigationBarAppearance = UINavigationBarAppearance()
    appearance.configureWithTransparentBackground()
    appearance.backgroundColor = color
    self.standardAppearance = appearance
    self.scrollEdgeAppearance = appearance
}

This works but isnt fast enough as the changes take effect only after the pushing animation ends. Any tip?

1

There are 1 best solutions below

1
Fabio On

Set your navigation bar with my extension:

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

call it in viewWillAppear or viewDidLoad and change your background colors, in your case set background to clear... How to use:

configureNavigationBar(largeTitleColor: .black, backgoundColor: .white, tintColor: .black, title: "yourTitle", preferredLargeTitle: true)

In your case, call configureNavigationBar func in viewWillAppear of start controller, call configureNavigationBar in viewDidLoad of destination controller.. ES: In SceneDelegate set your start controller under scene function:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let windowScene = (scene as? UIWindowScene) else { return }
    window = UIWindow(windowScene: windowScene)
    window?.makeKeyAndVisible()
    let controller = UINavigationController(rootViewController: StartController())
    window?.rootViewController = controller
    if #available(iOS 13, *) {
        window?.overrideUserInterfaceStyle = .dark
    }
}

this is StartController:

import UIKit

class StartController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    
    view.backgroundColor = .white
    navigationItem.rightBarButtonItem = UIBarButtonItem(title: "GO", style: .plain, target: self, action: #selector(handleGo))
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    configureNavigationBar(largeTitleColor: .white, backgoundColor: .black, tintColor: .white, title: "Start", preferredLargeTitle: true)
}

@objc fileprivate func handleGo() {
    let controller = DestinationController()
    navigationController?.pushViewController(controller, animated: true)
 }
}

This is DestinationController:

import UIKit

class DestinationController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    
    view.backgroundColor = .white
    configureNavigationBar(largeTitleColor: .white, backgoundColor: .red, tintColor: .white, title: "Destination", preferredLargeTitle: true)
 }
}

the result:

enter image description here