Skip to content

Instantly share code, notes, and snippets.

@aprato
Last active July 13, 2018 00:23
Show Gist options
  • Star 51 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save aprato/6631390 to your computer and use it in GitHub Desktop.
Save aprato/6631390 to your computer and use it in GitHub Desktop.
Darker iOS7 translucent UINavigationBar
// .h
@interface AMPNavigationBar : UINavigationBar
@property (nonatomic, assign) CGFloat extraColorLayerOpacity UI_APPEARANCE_SELECTOR;
@end
// .m
@interface AMPNavigationBar ()
@property (nonatomic, strong) CALayer *extraColorLayer;
@end
static CGFloat const kDefaultColorLayerOpacity = 0.5f;
@implementation AMPNavigationBar
- (void)setBarTintColor:(UIColor *)barTintColor
{
[super setBarTintColor:barTintColor];
if (self.extraColorLayer == nil) {
// this all only applies to 7.0 - 7.0.2
if ([[[UIDevice currentDevice] systemVersion] compare:@"7.0.3" options:NSNumericSearch] == NSOrderedAscending) {
self.extraColorLayer = [CALayer layer];
self.extraColorLayer.opacity = self.extraColorLayerOpacity;
[self.layer addSublayer:self.extraColorLayer];
}
}
self.extraColorLayer.backgroundColor = barTintColor.CGColor;
}
- (void)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);
}
}
- (void)setExtraColorLayerOpacity:(CGFloat)extraColorLayerOpacity
{
_extraColorLayerOpacity = extraColorLayerOpacity;
[self setNeedsLayout];
}
#pragma mark - NSCoding
- (id)initWithCoder:(NSCoder *)decoder
{
self = [super initWithCoder:decoder];
if (self) {
_extraColorLayerOpacity = [[decoder decodeObjectForKey:@"extraColorLayerOpacity"] floatValue];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[super encodeWithCoder:encoder];
[encoder encodeObject:@(self.extraColorLayerOpacity) forKey:@"extraColorLayerOpacity"];
}
@end
// the easy way to use it is to subclass UINavigationController and override:
- (id)initWithRootViewController:(UIViewController *)rootViewController
{
self = [super initWithNavigationBarClass:[APNavigationBar class] toolbarClass:[UIToolbar class]];
if (self) {
self.viewControllers = @[ rootViewController ];
}
return self;
}
@bkoc
Copy link

bkoc commented Sep 20, 2013

After pushing a new View Controller onto the Navigation controller, any bar button items get moved to be under this transparency layer.

We should reset the index of the layer in layoutSubviews to overcome this issue.

[self.layer insertSublayer:self.extraColorLayer atIndex:1];

@alanzeino
Copy link

Nice sleuthing @aprato and @bkoc! I took your answers, fixed a few things (nav bar height and early returns for versions prior to 7) and made a drop–in class here:

https://github.com/alanzeino/AZColoredNavigationBar

@cthomaschase
Copy link

Not sure if I'm implementing correctly, but I'm unable to get this to work, my colors are still muted. Any suggestions?

@cthomaschase
Copy link

Figured it out, user error. Thanks.

@aprato
Copy link
Author

aprato commented Sep 25, 2013

Thanks guys, when I wrote it wasn't pushing yet :) But i've updated it now, added UIAppearance for the opacity and support for NSCoding

@yichaowang
Copy link

Don't forget to add

[[APNavigationBar appearance] setExtraColorLayerOpacity:0.5f];

when you call it!

Maybe we can remove kDefaultColorLayerOpacity since it not used anymore.

@marcregan
Copy link

Nice work. Trying to figure out how to get this to work with UINavigationControllers created in a Storyboard. Not sure it's possible.

Your subclass of UINavigationController will have:

  • (id)initWithCoder:(NSCoder *)decoder

called when the subclass is created, but you don't have the opportunity to set the UINavigationBar at that time.

@sebschlo
Copy link

Thanks, great solution! Whenever I go into a subview or scroll the current view, though, the opacity seems to change after about 1 second. Is it supposed to do that? If so, how can I disable it?

@j-mcnally
Copy link

https://gist.github.com/j-mcnally/6987297

I didnt want to have to go over every single class and change the subclass so i went with the swizzing approach, id love if someone wanted to implement this to the original spec, its late and i only need it to be one color and one opacity ever, but wanted to get thoughts about potential pitfalls.

@chaitanyagupta
Copy link

This fails when the device is rotated to landscape orientation. Instead of computing spaceAboveBar from the bar's frame, its better to get the status bar's height directly using CGRectGetHeight([[UIApplication sharedApplication] statusBarFrame]). Fork here: https://gist.github.com/chaitanyagupta/7024530

@aprato
Copy link
Author

aprato commented Oct 25, 2013

check the new comment, 7.0.3 changed things again. I'm only using the backing layer pre 7.0.3. For everything later a color with an alpha (I'm using .91 on blackColor) seems to be a better approach.

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