50

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?

Dom Chapman
  • 953
  • 2
  • 10
  • 15

13 Answers13

141

Place this in your AppDelegate

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

Swift version with updates from comments

    UINavigationBar.appearance().shadowImage = UIImage()
    UINavigationBar.appearance().setBackgroundImage(UIImage(), forBarMetrics: .Default)
starball
  • 20,030
  • 7
  • 43
  • 238
Kevin Fernandes
  • 1,442
  • 1
  • 10
  • 2
  • 15
    NOTE: 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. – Mike Pollard Jun 04 '13 at 19:10
  • 1
    This is the right answer. `self.navigationBar.shadowImage = nil;` doesn't have the same effect as the technique described in the answer. – bithavoc Jul 18 '13 at 23:07
  • You may want to add an autorelease to the [[UIImage alloc] init] – Bas Holtrop Oct 07 '13 at 18:57
  • 6
    @BasHoltrop Autorelease? The Medieval period is gone. :) – Rudolf Adamkovič Jul 17 '14 at 10:35
  • 15
    The complete solution for >= iOS7 is -- `[[UINavigationBar appearance] setShadowImage:[UIImage new]]; [[UINavigationBar appearance] setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];` – gchbib Dec 19 '14 at 14:26
  • This helped me for **iPhone 6 Plus simulator** `[[UINavigationBar appearance] setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];` – Almas Adilbek Apr 15 '15 at 15:15
57

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;
Hank Brekke
  • 2,024
  • 2
  • 24
  • 33
25

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];
Lee Probert
  • 10,308
  • 8
  • 43
  • 70
19

Also you can try this:

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

controller is a UINavigationController.

Dima Korbin
  • 222
  • 2
  • 10
  • 4
    Remember this only works in iOS 6. From UINavigationBar.h: /* Default is nil. When non-nil, a custom shadow image to show instead of the default shadow image...*/ @property(nonatomic,retain) UIImage *shadowImage NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; – José M. Gilgado Dec 04 '12 at 07:35
7

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.

Steve Cotner
  • 505
  • 4
  • 14
6

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];
}
user1202579
  • 61
  • 1
  • 1
3

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

eager
  • 627
  • 6
  • 10
2

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 :)

xiaobo
  • 93
  • 8
1

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.

Richard Williams
  • 337
  • 1
  • 5
  • 12
1

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.

CharlesA
  • 4,260
  • 2
  • 25
  • 31
1

How about the alternative way:

UINavigationBar.appearance().barStyle = .Black

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

SoftDesigner
  • 5,640
  • 3
  • 58
  • 47
0

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 
Titouan de Bailleul
  • 12,920
  • 11
  • 66
  • 121
Dejan Balaban
  • 461
  • 4
  • 11
0

In Swift 3.0 this would look like this

UINavigationBar.appearance().shadowImage = UIImage ()
UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)
Kumar C
  • 525
  • 6
  • 21