Skip to content

Instantly share code, notes, and snippets.

@Sorix
Created April 12, 2017 14:13
Show Gist options
  • Save Sorix/1d8543b18cfd76c12c36525bc280a35d to your computer and use it in GitHub Desktop.
Save Sorix/1d8543b18cfd76c12c36525bc280a35d to your computer and use it in GitHub Desktop.
Colourable UINavigationController that supports different colors for navigation bars among different view controllers

Overview

ColorableNavigationController is tiny subclass of UINavigationController that supports different colors for navigation bars among different view controllers.

Code snippet was created to answer on my question at Stackoverflow.

How to install

Just add code from ColorableNavigationController.swift or copy that file to your project.

Usage

  1. Use ColorableNavigationController as your root UINavigationController (you can init it by yourself or change in Storyboard).
  2. Adopt needed child view controllers to NavigationBarColorable protocol:

Colors will be set automatically on push or pop actions. Please don't forget to set initial values of barTintColor and tintColor on first load of UINavigationController.

Protocol

public protocol NavigationBarColorable: class {
	var navigationTintColor: UIColor? { get } // optional
	var navigationBarTintColor: UIColor? { get }
}

Example

class ViewControllerA: UIViewController, NavigationBarColorable {
	public var navigationBarTintColor: UIColor? { return UIColor.blue }

	override func viewDidLoad() {
		super.viewDidLoad()

		navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Push", style: .plain, target: self, action: #selector(self.showController))
	}

	func showController() {
		navigationController?.pushViewController(ViewControllerB(), animated: true)
	}
}

class ViewControllerB: UIViewController, NavigationBarColorable {
	public var navigationBarTintColor: UIColor? { return UIColor.red }
}

let navigationController = ColorableNavigationController(rootViewController: ViewControllerA())
//
// ColorableNavigationController.swift
//
// Created by Vasily Ulianov on 26.10.16.
//
import UIKit
/// Navigation bar colors for `ColorableNavigationController`, called on `push` & `pop` actions
public protocol NavigationBarColorable: class {
var navigationTintColor: UIColor? { get }
var navigationBarTintColor: UIColor? { get }
}
public extension NavigationBarColorable {
var navigationTintColor: UIColor? { return nil }
}
/**
UINavigationController with different colors support of UINavigationBar.
To use it please adopt needed child view controllers to protocol `NavigationBarColorable`.
- note: Don't forget to set initial tint and barTint colors
*/
open class ColorableNavigationController: UINavigationController {
private var previousViewController: UIViewController? {
guard viewControllers.count > 1 else {
return nil
}
return viewControllers[viewControllers.count - 2]
}
override open func pushViewController(_ viewController: UIViewController, animated: Bool) {
if let colors = viewController as? NavigationBarColorable {
self.setNavigationBarColors(colors)
}
super.pushViewController(viewController, animated: animated)
}
override open func popViewController(animated: Bool) -> UIViewController? {
if let colors = self.previousViewController as? NavigationBarColorable {
self.setNavigationBarColors(colors)
}
// Let's start pop action or we can't get transitionCoordinator()
let popViewController = super.popViewController(animated: animated)
// Secure situation if user cancelled transition
transitionCoordinator?.animate(alongsideTransition: nil, completion: { [weak self] (context) in
guard let colors = self?.topViewController as? NavigationBarColorable else { return }
self?.setNavigationBarColors(colors)
})
return popViewController
}
private func setNavigationBarColors(_ colors: NavigationBarColorable) {
if let tintColor = colors.navigationTintColor {
self.navigationBar.tintColor = tintColor
}
self.navigationBar.barTintColor = colors.navigationBarTintColor
}
}
@nmdias
Copy link

nmdias commented May 28, 2018

Oh yeah, that's the stuff 🤤

Thanks

@AntonReality
Copy link

AntonReality commented May 30, 2018

You can also set the initial style on viewdidLoad for rootViewController like this:

override func viewDidLoad() {
        super.viewDidLoad()

        guard let rootViewController = viewControllers.first as? NavigationBarColorable else { return }
        setNavigationBarColors(rootViewController)
}

@max-pfeiffer
Copy link

@SoriS, thanks a lot for this gist. Yesterday I spent hours trying to solve this using the UINavigationControllerDelegate willShow viewController function. Could not get it to work properly.

This solution works fine on iOS 11.

@andreaagudo3
Copy link

Does this work with transparent navigation bar? I mean backgroundColor = .clear

@NiltiakSivad
Copy link

This does not work for me. It changes the colors, but the animations are janky. I was testing with a transparent bar though to a colored bar (I added the additional properties to the protocol then set the new properties in the setNavigationBarColors method).

@iNono22
Copy link

iNono22 commented Aug 1, 2018

Does not work with pattern Color like Gradient
kCGColorSpaceModelPattern

@ApoorvKhatreja
Copy link

Indeed, echoing what @NiltiakSivad said, this also seems to break transitions from a large nav bar title to a view that has a regular nav bar title.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment