b2cloud

11th October 2013

Custom UINavigationBar colors in iOS7

Guides | Tutorial By 4 years ago

Note: The formula for finding the color values has changed, see the updated post. This post’s formulas are not up to date, but the process remains the same

The other week I compiled a calculator to determine what bar tint color to set a UINavigationBar to if you want the blur effect on iOS 7. This works well, but only if your RGB values are >= 102 – because Apple adjusts the RGB you set the bar to.

Lately I’ve been experimenting with darker colors, those with RGB values under 102. There is a work around, however it requires some tricks.

I am going to demonstrate how to get the Facebook bar color of (65, 96, 156), which you will notice the red and green channels are under the ideal 102 threshold.

Before I begin, I will mention that if you are reading this and your RGB values are >= 102, don’t go any further. Instead use my calculator. The trade off of making a darker bar is that the blur will be less blurry, and the darker you go, the worse it gets. If you plan on a jet black bar with a blur, at the moment I don’t think that’s entirely possible.

The setup

Now, I haven’t figured out any mathematical equations to this as I did with my calculator, but the process is as follows. First, use the original calculator and specify the values given as the bar’s tint color, even if they are clipped. I’ve added a red and green background view so you can see the blur effect on the let and right sides behind the bar.

UINavigationBar* navigationBar = navigationController.navigationBar;
[navigationBar setBarTintColor:[UIColor colorWithRed:0.0f green:0.0f blue:90.0f/255.0f alpha:1]];

You can see I’ve got optimal blur, but the color isn’t right (102, 102, 156).

The underlay

Now, you want to add an underlay view that will darken things up. Start with black with 0 alpha, then adjust each color channel and alpha in conjunction with the bar tint color until you get the desired effect. Note that the goal of this is to have the alpha as low as possible, otherwise you will block out the background completely. I used trial and error to get the desired RGBA values on my underlay. The underlay should be inserted at index 1.

Iteration 1 (102, 102, 156) – (the same):

UINavigationBar* navigationBar = navigationController.navigationBar;

[navigationBar setBarTintColor:[UIColor colorWithRed:0.0f green:0.0f blue:90.0f/255.0f alpha:1]];

const CGFloat statusBarHeight = 20;    //  Make this dynamic in your own code...

UIView* underlayView = [[UIView alloc] initWithFrame:CGRectMake(0, -statusBarHeight, navigationBar.frame.size.width, navigationBar.frame.size.height + statusBarHeight)];
[underlayView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
[underlayView setBackgroundColor:[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:1.0f]];
[underlayView setAlpha:0.0f];
[navigationBar insertSubview:underlayView atIndex:1];

Iteration 2 (65, 65, 99), red channel correct:

UINavigationBar* navigationBar = navigationController.navigationBar;

[navigationBar setBarTintColor:[UIColor colorWithRed:0.0f green:0.0f blue:90.0f/255.0f alpha:1]];

const CGFloat statusBarHeight = 20;    //  Make this dynamic in your own code...

UIView* underlayView = [[UIView alloc] initWithFrame:CGRectMake(0, -statusBarHeight, navigationBar.frame.size.width, navigationBar.frame.size.height + statusBarHeight)];
[underlayView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
[underlayView setBackgroundColor:[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:1.0f]];
[underlayView setAlpha:0.36f];
[navigationBar insertSubview:underlayView atIndex:1];

Iteration 3 (65, 96, 99), red and green channels correct:

UINavigationBar* navigationBar = navigationController.navigationBar;

[navigationBar setBarTintColor:[UIColor colorWithRed:0.0f green:0.0f blue:90.0f/255.0f alpha:1]];

const CGFloat statusBarHeight = 20;    //  Make this dynamic in your own code...

UIView* underlayView = [[UIView alloc] initWithFrame:CGRectMake(0, -statusBarHeight, navigationBar.frame.size.width, navigationBar.frame.size.height + statusBarHeight)];
[underlayView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
[underlayView setBackgroundColor:[UIColor colorWithRed:0.0f green:0.34f blue:0.0f alpha:1.0f]];
[underlayView setAlpha:0.36f];
[navigationBar insertSubview:underlayView atIndex:1];

Iteration 4 (65, 95, 156), all channels correct:

UINavigationBar* navigationBar = navigationController.navigationBar;

[navigationBar setBarTintColor:[UIColor colorWithRed:0.0f green:0.0f blue:90.0f/255.0f alpha:1]];

const CGFloat statusBarHeight = 20;    //  Make this dynamic in your own code...

UIView* underlayView = [[UIView alloc] initWithFrame:CGRectMake(0, -statusBarHeight, navigationBar.frame.size.width, navigationBar.frame.size.height + statusBarHeight)];
[underlayView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
[underlayView setBackgroundColor:[UIColor colorWithRed:0.0f green:0.34f blue:0.62f alpha:1.0f]];
[underlayView setAlpha:0.36f];
[navigationBar insertSubview:underlayView atIndex:1];

The fixin’

If you run your project and push a view controller on top of the stack you will notice the ‘underlay’ goes on top of the back button, and even the title. To prevent this we’re going to move our underlay view into a UINavigationBar subclass, which will reposition it to the correct index whenever a new subview is added.

Create the subclass. It will simply manage the underlay and force it to always reside at index 1 in the view hierarchy.

UnderlayNavigationBar.h

@interface UnderlayNavigationBar : UINavigationBar

@end

UnderlayNavigationBar.m

@interface UnderlayNavigationBar ()
{
	UIView* _underlayView;
}

- (UIView*) underlayView;

@end

@implementation UnderlayNavigationBar

- (void) didAddSubview:(UIView *)subview
{
	[super didAddSubview:subview];

	if(subview != _underlayView)
	{
		UIView* underlayView = self.underlayView;
		[underlayView removeFromSuperview];
		[self insertSubview:underlayView atIndex:1];
	}
}

- (UIView*) underlayView
{
	if(_underlayView == nil)
	{
		const CGFloat statusBarHeight = 20;    //  Make this dynamic in your own code...
		const CGSize selfSize = self.frame.size;

		_underlayView = [[UIView alloc] initWithFrame:CGRectMake(0, -statusBarHeight, selfSize.width, selfSize.height + statusBarHeight)];
		[_underlayView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
		[_underlayView setBackgroundColor:[UIColor colorWithRed:0.0f green:0.34f blue:0.62f alpha:1.0f]];
		[_underlayView setAlpha:0.36f];
		[_underlayView setUserInteractionEnabled:NO];
	}

	return _underlayView;
}

@end

You will need to set your UINavigationController to use this subclass bar, in Interface Builder just change the class, or in code instantiate your navigation controller like so:

UIViewController* rootViewController = ...;
UINavigationController* navigationController = [[UINavigationController alloc] initWithNavigationBarClass:[UnderlayNavigationBar class] toolbarClass:nil];
[navigationController.navigationBar setBarTintColor:[UIColor colorWithRed:0.0f green:0.0f blue:90.0f/255.0f alpha:1]];
[navigationController setViewControllers:@[rootViewController]];

That should be all, now your navigation bar will blur with a darker color than the calculator allows for. I haven’t accounted for iOS6 compatibility in these snippets of code, so some things may need to be adjusted.

  • Jorge Costa

    just amazing!! Thanks Tom!

  • ayberkt

    As of iOS 7.1, is this article still viable? Thanks!

    • Tom

      I believe Apple changed the behaviour, the values are now a little bit different to get the same effect.

  • Simina Dorin

    Is there a way to remove the 1px shadow because using this approach and adding [self setShadowImage:[UIImage new]]; does not seem to work?

Recommended Posts

Bar color calculator for iOS7 and iOS8 (UIToolbar and UINavigationBar)

Post by 4 years ago

In iOS7 a translucent UIToolbar or UINavigationBar will now blur the content behind it. The bar’s color can still be set (now with the -setBarTintColor: compared to the previous -setTintColor:), however the UIColor you set...

Connect With Us

We're trusted by some of the largest businesses and enterprises to build digital products that matter.