50
votes

It seems in iOS 6, a drop shadow is automatically added to the navigation bar even when you set a custom background image. I'm pretty sure this wasn't the case with iOS 5 as when I test the same code in the iOS 5 and 6 sim, the shadow appears in iOS 6 but not 5.

Does anyone know anything about this? Or how to enable/disable it?

13
iOS 6 is beta, pre-release software and I'm pretty sure that it's under NDA right now.Alladinian
I would delete this question to avoid getting all the NDA comments and post it on the dev forums provided by Apple for pre-release software. devforums.apple.com/community/iosBill Burgess
Look at the UI customization session video againDavid Rönnqvist
David is right - the UI customisation video covers this exact topic.Cthutu

13 Answers

141
votes

Place this in your AppDelegate

[[UINavigationBar appearance] setShadowImage:[UIImage new]];
// is IOS 7 and later
[[UINavigationBar appearance] setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];

This is what did it for me. Hope it helps!

Swift version with updates from comments

    UINavigationBar.appearance().shadowImage = UIImage()
    UINavigationBar.appearance().setBackgroundImage(UIImage(), forBarMetrics: .Default)
57
votes

I know this has been solved with more complicated answers above, but this is the quickest and easiest way I hid the shadow under the navigation bar.

self.navigationController.navigationBar.clipsToBounds = YES;
25
votes

Note from the Apple dev docs on the subject of the shadowImage property:

Discussion: The default value is nil, which corresponds to the default shadow image. When non-nil, this property represents a custom shadow image to show instead of the default. For a custom shadow image to be shown, a custom background image must also be set with the setBackgroundImage:forBarMetrics: method. If the default background image is used, then the default shadow image will be used regardless of the value of this property.

So to use the nil UIImage hack you must also be setting a custom nav bar background image. This can be a nil image too, which results in a nice flat, clean 'metro' style nav bar :

[[UINavigationBar appearance] setShadowImage:[[UIImage alloc] init]];
        [[UINavigationBar appearance] setBackgroundImage:[[UIImage alloc] init] forBarMetrics:UIBarMetricsDefault];
19
votes

Also you can try this:

controller.navigationBar.shadowImage = [[[UIImage alloc] init] autorelease];

controller is a UINavigationController.

7
votes

General, non-NDA-infringing answer:

If you don't want something sticking out of a layer, mask the layer to its bounds.

[self.layer setMasksToBounds:YES];

Set the height explicitly to 44 (or 32 for landscape on iPhone) if that doesn't work on its own.

6
votes

Setting the shadowImage to a null image does work, however, the way the solution is presented results in adding a property if the OS is earlier than iOS 6.

A better way to do something that is dependent on the existence of a property or method is:

if ([self.navigationController.navigationBar
respondsToSelector:@selector(shadowImage)]) {
self.navigationController.navigationBar.shadowImage = [[[UIImage alloc] init] autorelease];
}
3
votes

There are two possible solutions, the second of which is mentioned in other answers.

  1. Add a single, transparent, pixel at the bottom of your navigation bar background image, making it 45pt tall. This disables the shadows in iOS 6.
  2. Implement the following code:

    // Omit the conditional if minimum OS is iOS 6 or above
    if ([UINavigationBar instancesRespondToSelector:@selector(setShadowImage:)]) {
        [[UINavigationBar appearance] setShadowImage:[[UIImage alloc] init]];
    }
    

Source: Advanced Appearance Customization on iOS, @27:15

2
votes

Since self.navigationController.navigationBar.shadowImage = [[UIImage alloc] init]; not working, I've found an easy and workable way to remove the shadow of UINavigationBar in both iOS 6 AND iOS 5. Hope people who need can see this post.

All you have to do is prepare one background image that the height is 1 pixel larger than your navigation bar height (e.g. 320×45 for default UINavigationBar, 640×90 for 2x of course).

Then just use [[UINavigationBar appearance] setBackgroundImage: ...], you will find shadow is replaced by that 1 pixel. cheers!

BTW I found Twitter has done the exactly same thing, if you unzip Twitter.ipa and look into bg_nav_bar_events_dark.png, the size is 320×47. They made their own shadow for 3 pixels :)

1
votes

I cannot comment so I'll add my information here.

Perhaps the above suggestions worked in the beta, but it does not seem to be the case now.

self.navigationController.navigationBar.shadowImage = [[UIImage alloc] init];

The above does not work, neither do any of the other similar answers above. I have tried them all.

Clipping to bounds does work but doesn't give the result I want as I'd like other views to hang outside the nav bar.

1
votes

I came across this SO question when trying to get nav bars to look the same between iOS6 and iOS7.

The answer I found worked was simply to use:

    NSMutableDictionary *titleBarAttributes = [NSMutableDictionary dictionaryWithDictionary: [[UINavigationBar appearance] titleTextAttributes]];
    [titleBarAttributes setValue:[NSNumber numberWithInt:0] forKey:UITextAttributeTextShadowOffset];
    [[UINavigationBar appearance] setTitleTextAttributes:titleBarAttributes];

ie: set the shadow offset to zero.

1
votes

How about the alternative way:

UINavigationBar.appearance().barStyle = .Black

For the dark navigation bars iOS doesn't show the shadow.

0
votes

I had the same problem and I've solved it by following:

CustomNavBar *navBar = (CustomNavBar *)self.navigationController.navigationBar;
        [navBar setBackgroundImage:[UIImage imageNamed:@"navigation_bar_gray.png"] forBarMetrics:UIBarMetricsDefault];
        navBar.shadowImage = [[UIImage alloc]init]; // this is what acctually removed the shadow under navigation bar 
0
votes

In Swift 3.0 this would look like this

UINavigationBar.appearance().shadowImage = UIImage ()
UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)