Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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

This comment has been minimized.

Copy link

@bkoc 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

This comment has been minimized.

Copy link

@alanzeino alanzeino commented Sep 23, 2013

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

This comment has been minimized.

Copy link

@cthomaschase cthomaschase commented Sep 23, 2013

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

@cthomaschase

This comment has been minimized.

Copy link

@cthomaschase cthomaschase commented Sep 23, 2013

Figured it out, user error. Thanks.

@aprato

This comment has been minimized.

Copy link
Owner Author

@aprato 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

This comment has been minimized.

Copy link

@yichaowang yichaowang commented Oct 3, 2013

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

This comment has been minimized.

Copy link

@marcregan marcregan commented Oct 6, 2013

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.

@sschloesser

This comment has been minimized.

Copy link

@sschloesser sschloesser commented Oct 10, 2013

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

This comment has been minimized.

Copy link

@j-mcnally j-mcnally commented Oct 15, 2013

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

This comment has been minimized.

Copy link

@chaitanyagupta chaitanyagupta commented Oct 17, 2013

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

This comment has been minimized.

Copy link
Owner Author

@aprato 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