Achieving bright, vivid colors for an iOS 7 translucent UINavigationBar

49,925

Solution 1

iOS 7.0.3 UPDATE: As you see above 7.0.3 changed things. I've updated my gist. Hopefully this will just go away as people upgrade.

Original Answer: I ended up with a hack combining the two of the other answers. I'm subclassing UINavigationBar and adding a layer to the back with some extra space to cover if any of the various height status bars are up. The layer gets adjusted in layout subviews and the color changes whenever you set barTintColor.

Gist: https://gist.github.com/aprato/6631390

setBarTintColor

  [super setBarTintColor:barTintColor];
  if (self.extraColorLayer == nil) {
    self.extraColorLayer = [CALayer layer];
    self.extraColorLayer.opacity = self.extraColorLayerOpacity;
    [self.layer addSublayer:self.extraColorLayer];
  }
  self.extraColorLayer.backgroundColor = barTintColor.CGColor;

layoutSubviews

  [super layoutSubviews];
  if (self.extraColorLayer != nil) {
    [self.extraColorLayer removeFromSuperlayer];
    self.extraColorLayer.opacity = self.extraColorLayerOpacity;
    [self.layer insertSublayer:self.extraColorLayer atIndex:1];
    CGFloat spaceAboveBar = self.frame.origin.y;
    self.extraColorLayer.frame = CGRectMake(0, 0 - spaceAboveBar, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds) + spaceAboveBar);
  }

Solution 2

The behavior of tintColor for bars has changed on iOS 7.0. It no longer affects the bar's background and behaves as described for the tintColor property added to UIView. To tint the bar's background, please use -barTintColor.You can use following code to make the app work with both ios6 and ios7.

if(IS_IOS7)
{
    self.navigationController.navigationBar.barTintColor = [UIColor blackColor];
    self.navigationController.navigationBar.translucent = NO;
}
else
{
    self.navigationController.navigationBar.tintColor = [UIColor blackColor];
}

IS_IOS7 is a macro which is defined in pch file as follows.

#define IS_IOS7 ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0)

Solution 3

I didn't come up with this solution but it seems to work fairly well. I just added it to viewDidLoad on my subclass of UINavigationController.

Source: https://gist.github.com/alanzeino/6619253

// cheers to @stroughtonsmith for helping out with this one

UIColor *barColour = [UIColor colorWithRed:0.13f green:0.14f blue:0.15f alpha:1.00f];
UIView *colourView = [[UIView alloc] initWithFrame:CGRectMake(0.f, -20.f, 320.f, 64.f)];
colourView.opaque = NO;
colourView.alpha = .7f;
colourView.backgroundColor = barColour;
self.navigationBar.barTintColor = barColour;
[self.navigationBar.layer insertSublayer:colourView.layer atIndex:1];

Solution 4

One low-fi way would probably be pinning a UIView that is the height of the Navigation Bar to the top of the view behind the bar. Make that view the same color as the navigation bar but play with the alpha until you get the desired effects:

UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.navigationController.navigationBar.frame), 64)];
    backgroundView.backgroundColor = [UIColor colorWithRed:0.0 green:0.0 blue:1 alpha:.5];

[self.navigationController.view insertSubview:backgroundView belowSubview:self.navigationController.navigationBar];

UIView behind

enter image description here

(Changed color from lower examples to emphasis transparency. Transparency/blurring is more noticeable when in movement.)

Subclassing the UINavigationBar and placing that same view above the background but behind everything else will probably achieve similar results while being less hacky.


Another solution I've seen tossed around is playing with the alpha of the UINavigationBar:

self.navigationController.navigationBar.alpha = 0.5f;

Edit: Actually, after testing it seems like this doesn't provide the intend behavior (or any behavior):

.8 alpha

Navigation bar with .8 alpha

Unadjusted alpha

enter image description here

Obviously, you will only want to do this on iOS 7 devices. So, add some version check before you implement any of these.

Solution 5

Instead of creating your UIColor object in the RGB format, use HSB and increase the saturation parameter. (Credits to Sam Soffes who describes this method here)

navigationBar.barTintColor = [UIColor colorWithHue:0.555f saturation:1.f brightness:0.855f alpha:1.f];

Note: This solution is a tradeoff and doesn't work well for colors with high saturation.

To pick the HSB color from your design you can use a tool like ColorSnapper which allows you to simply copy the UIColor HSB format.

You can also try the UIColor Category (GitHub Link) from David Keegan to modify existing colors.

Share:
49,925

Related videos on Youtube

SpacePyro
Author by

SpacePyro

UT Austin alum. Programming, videogame, and anime enthusiast. I also love a nice cup of coffee. I'm a Swift / Objective-C guru with enough knowledge of Ruby to live dangerously. I'm also the creator of MyAniList.

Updated on July 08, 2022

Comments

  • SpacePyro
    SpacePyro almost 2 years

    iOS 7.1 UPDATE: Looks like the workaround for modifying the alpha channel in the UINavigationBar has been ignored in this update. Right now, the best solution seems to be to just 'deal with it' and hope that whatever color you choose can render a translucent effect. I am still looking into ways of getting around this.


    iOS 7.0.3 UPDATE: The GitHub library we created has been updated to slightly work around this issue when using iOS 7.0.3. Unfortunately, there is no magic formula to support both colors created in iOS 7.0.2 and earlier and iOS 7.0.3. Seems like Apple improved the saturation, but at the cost of opacity (since the blurred translucency is dependant on the opacity level). I, along with a few others, are working on creating a much better fix for this.


    I'm sure many people have already come across the problem where iOS 7 tends to desaturate the color of a UINavigationBar that is translucent.

    My goal is to achieve a UINavigationBar with this tint color, but translucent:

    UINavigationBar, Opaque

    However, with translucency, I'm getting this. The background view is white, which I understand will make this view a bit lighter:

    UINavigationBar, Translucent

    Is there any way to achieve the original color while still having translucency? I've noticed Facebook has been able to get their bar to be their rich, blue color, as displayed here:

    Facebook UINavigationBar, Translucent

    ..so I know there has to be some way. Background views obviously make a difference here, but most of their content is also gray/white. It seems that regardless of whatever bar tint color you put in, you are unable to get vivid colors under translucency.

    Updated with solution.

    Here's the solution that I ended up coming up with. I took aprato's solution and then encompassed the custom UINavigationBar within a UINavigationController subclass. I have created a repository that has this implementation listed below, along with an example app.

    ////////////////////////////
    // CRNavigationBar.m
    ////////////////////////////
    
    #import "CRNavigationBar.h"
    
    @interface CRNavigationBar ()
    @property (nonatomic, strong) CALayer *colorLayer;
    @end
    
    @implementation CRNavigationBar
    
    static CGFloat const kDefaultColorLayerOpacity = 0.5f;
    static CGFloat const kSpaceToCoverStatusBars = 20.0f;
    
    - (void)setBarTintColor:(UIColor *)barTintColor {
        [super setBarTintColor:barTintColor];
        if (self.colorLayer == nil) {
            self.colorLayer = [CALayer layer];
            self.colorLayer.opacity = kDefaultColorLayerOpacity;
            [self.layer addSublayer:self.colorLayer];
        }
        self.colorLayer.backgroundColor = barTintColor.CGColor;
    }
    
    - (void)layoutSubviews {
        [super layoutSubviews];
        if (self.colorLayer != nil) {
            self.colorLayer.frame = CGRectMake(0, 0 - kSpaceToCoverStatusBars, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds) + kSpaceToCoverStatusBars);
    
            [self.layer insertSublayer:self.colorLayer atIndex:1];
        }
    }
    
    @end
    

    ////////////////////////////
    // CRNavigationController.m
    ////////////////////////////
    
    #import "CRNavigationController.h"
    #import "CRNavigationBar.h"
    
    @interface CRNavigationController ()
    
    @end
    
    @implementation CRNavigationController
    
    - (id)init {
        self = [super initWithNavigationBarClass:[CRNavigationBar class] toolbarClass:nil];
        if(self) {
            // Custom initialization here, if needed.    
        }
        return self;
    }
    
    - (id)initWithRootViewController:(UIViewController *)rootViewController {
        self = [super initWithNavigationBarClass:[CRNavigationBar class] toolbarClass:nil];
        if(self) {
            self.viewControllers = @[rootViewController];
        }
    
        return self;
    }
    
    @end
    
    • Vinzzz
      Vinzzz over 10 years
      isn't Facebook iOS7 UINAvigationBar opaque ?
    • Kevin
      Kevin over 10 years
      Nope, it is a much more subtle transparency then the default iOS. Much better, IMO.
    • LE SANG
      LE SANG over 10 years
      Facebook NavigationBar not transparent
    • SpacePyro
      SpacePyro over 10 years
      It is definitely translucent; please see my edited response.
    • Kevin
      Kevin over 10 years
      It might not be enable on all devices, but the NewFeed definitely is for the iPhone 5: Screenshot
    • Jeremy
      Jeremy over 10 years
      I'm having the same issue. I would love to know how they did it...
    • Dejell
      Dejell over 10 years
      I tried your solution for the color 0,52,72 but it doesn't come out like in photoshop for instance..
    • SpacePyro
      SpacePyro over 10 years
      @Odelya - This is not a solution to obtain the correct colors, but rather a solution to correct the lightness of the UINavigationBar as best as possible when exposed to translucency in iOS 7.
    • Vinzzz
      Vinzzz over 10 years
      Indeed, it's fully opaque on iPhone 4, and iPad 3rd gen. Just like Control Center, and Notification Center which don't use the glass-blur effect on these devices. on Iphone 4S, it's enabled
    • Rivera
      Rivera over 10 years
      I get lots of flickering with this method (and the library).
    • James Matthew Mudgett
      James Matthew Mudgett about 10 years
      For 7.1 I found that it still works but you can no longer override the calls made through the appearance proxy. It's a bit hacky but I instead implemented an observer pattern in the subclassed navigation bar to update the color or background image layer (repeat header background) from within the navigation bar.
    • Andrew
      Andrew almost 10 years
      How can this be achieved in iOS 8?
    • Andy Tsen
      Andy Tsen almost 10 years
      Is there any fix for this yet? The spotify app nav bars create this effect extremely well.
    • Gank
      Gank over 8 years
      How to use it with storyboard? I've set CRNavigationController.h as custom class of the storyborad root view. But it can't run into the CRNavigationController.m.
  • StuartM
    StuartM over 10 years
    I second the approach of updating the alpha and the tintColor to get the color that you are looking for.
  • Kevin
    Kevin over 10 years
    If you can get the desired effects with that less invasive way, then totally. But I'm not sure how effective it will be.
  • SpacePyro
    SpacePyro over 10 years
    The only issue I'm getting with this (and it doesn't seem represented in your picture) is that it's adding the view directly on top the navigation bar, and as a result, it's causing the controls to be blocked: cl.ly/image/3d3C2o0N283S
  • Kevin
    Kevin over 10 years
    Whoops, now I see. Should be [self.navigationController.view insertSubview:backgroundView belowSubview:self.navigationController.navigationBar];
  • SpacePyro
    SpacePyro over 10 years
    Yeah, I figured that's what you meant. ;) This definitely gives me a better span of colors to pick from. Getting the vibrance of the original color will probably be impossible considering that blur effect still really desaturates anything under it. I'll just have to live with a darker color for now.
  • Evan Davis
    Evan Davis over 10 years
    It looks like after pushing a new View Controller onto the Navigation controller, any bar button items get moved to be under this transparency layer.
  • Nick Locking
    Nick Locking over 10 years
    Titles get obscured by this transparency layer, too.
  • Léo Natan
    Léo Natan over 10 years
    This does not retain blur however. There is transparency, but no blur.
  • Léo Natan
    Léo Natan over 10 years
    But the question requires translucency as well.
  • Jeremy
    Jeremy over 10 years
    Where did you implement this on the ViewController? Under viewDidLoad?
  • SpacePyro
    SpacePyro over 10 years
    This works beautifully! I decided to add this to my UINavigationController subclass. Here's a gist of it for those who are interested in having a custom UINavigationBar within a UINavigationController.
  • Alan Zeino
    Alan Zeino over 10 years
    I wrote this gist. The comments are right; either button items are pushed underneath when a new view controller is pushed, or the sublayers are inheriting the alpha of colourView. I forked the other answer and fixed a few things into a new drop–in class: github.com/alanzeino/AZColoredNavigationBar
  • Anthony
    Anthony over 10 years
    @Jeremy like SpacePyro I have a nav controller subclass (which like the bar I was using pre 7 for UIAppearance targeting) that overrides initWithRootViewController to use this. I've updated my gist
  • Jeremy
    Jeremy over 10 years
    Um…okay..I think I need to see this in an entire test project to fully see what you mean. I was able to replicate timeuser's example below but I'm still a little green with this one.
  • Anthony
    Anthony over 10 years
    @Jeremy check out the question again. SpacePyro updated it with my answer. You just need to add two new classes to your project, the first a UINavigationController subclass and the other a UINavigationBar subclass. Drop the code in and changes all the places you create a UINavigationController to use your new subclass. If you are using interface builder pick your navigation controller, select the Identity Inspector, you should see a field at the top named 'Class' where you can pick your new subclass.
  • Mr. T
    Mr. T over 10 years
    @aprato I used your solution but found a few corner cases where the new layers (eg. UINavigationItemButtonViews, UINavigationItemViews, etc) would be automatically inserted into a position below the extraColorLayer (which would cause those title or button elements to be affected by the extraColorLayer and thus fainter in color than they normally would be). So I adjusted your solution to force the extraColorLayer to stay at the index position 1. At index position 1, the extraColorLayer stays right above the _UINavigationBarBackground, but underneath everything else. See my solution below.
  • Jeremy
    Jeremy over 10 years
    @aprato - Thanks for that. Der. Got it working sorta. Still not coming out the right color for me but I feel it's not because of the suggested solution more so my implementation of it or something. I will put together a project and post it to see where I'm going wrong.
  • SpacePyro
    SpacePyro over 10 years
    Hey Jeremy, I just created a demo app/repo for this. You can take a look here.
  • Jeremy
    Jeremy over 10 years
    Hey @SpacePyro, I was just about to post what I have done. Have a look as I have put in exactly the solution above with results slightly different from others. Clearly I'm doing something wrong here. github.com/jermowatson/ColorTabs.git. I'll review yours too!
  • Anthony
    Anthony over 10 years
    @Mr.T Thanks! When I wrote it wasn't pushing yet :) But i've updated it now, added UIAppearance for the opacity and support for NSCoding
  • Nick Locking
    Nick Locking over 10 years
    If you're trying to match iOS 7's style, using a drop shadow on your title text is probably inappropriate.
  • Jeremy
    Jeremy over 10 years
    @SpacePyro - GOT IT! I was using a storyboard and clearly missing calling the function into the viewDidLoad etc etc. Nice work everyone! Thanks again it was doing my head in!
  • bryguy1300
    bryguy1300 over 10 years
    You're correct, in iOS 7 Apple is no longer setting shadows on their navbar text. If you wanted a custom look though, this would give you the option.
  • Rob van der Veer
    Rob van der Veer over 10 years
    Since layers and subviews aren't mixed, i thing overloading addSubView isn't necessary
  • SpacePyro
    SpacePyro over 10 years
    Couldn't you just wrap your UIViewController inside a UINavigationController with [[[UINavigationController alloc] initWithRootViewController:<YourViewController>]? This may also be better answered as a separate question, rather than posting here.
  • Dejell
    Dejell over 10 years
    @aprato I tried your solution but it doesn't work. I used CRNavigationController, but still the color is dimmed
  • Anthony
    Anthony over 10 years
    @Odelya are you using a storyboard? I haven't tested that yet but there are a few comments on the subject on my gist. I'll try to take a look soon
  • Bilbo Baggins
    Bilbo Baggins over 10 years
    :) Thanks, it worked for me. However there is a 1px black bottom border under the nav bar. Is there any way to remove it?
  • Matej Bukovinski
    Matej Bukovinski over 10 years
    Yes, their tint color implementation has definitely changed in 7.0.3. I can now achieve my desired color by just bumping the saturation up by about 10-15%. Before I also needed to ad the extra layer.
  • SpacePyro
    SpacePyro over 10 years
    Thanks for letting me know! I'll be pushing an update to my library for this.
  • Juan Zamora
    Juan Zamora over 10 years
    I was able to put the text white by selecting the Translucent Black Navigation Bar style... this did the trick for me. Also set the yourBar.tintColor = [UIColor whiteColor]; for other custom buttons on the top.
  • Rivera
    Rivera over 10 years
    Unless I'm missing something, this library's bars are not translucent.
  • Léo Natan
    Léo Natan about 10 years
    And unfixed in 7.1 >_<
  • Andrew
    Andrew almost 10 years
    How can this be achieved in iOS 8?
  • Allen Hsu
    Allen Hsu over 9 years
    @JordanBrown not tested on 7.1 & 8
  • Ortwin Gentz
    Ortwin Gentz almost 9 years
    This doesn't give you blur.