How to hide UINavigationBar 1px bottom line

212,218

Solution 1

For iOS 13:

Use the .shadowColor property

If this property is nil or contains the clear color, the bar displays no shadow

For instance:

let navigationBar = navigationController?.navigationBar
let navigationBarAppearance = UINavigationBarAppearance()
navigationBarAppearance.shadowColor = .clear
navigationBar?.scrollEdgeAppearance = navigationBarAppearance

For iOS 12 and below:

To do this, you should set a custom shadow image. But for the shadow image to be shown you also need to set a custom background image, quote from Apple's documentation:

For a custom shadow image to be shown, a custom background image must also be set with the setBackgroundImage(_:for:) method. If the default background image is used, then the default shadow image will be used regardless of the value of this property.

So:

let navigationBar = navigationController!.navigationBar
navigationBar.setBackgroundImage(#imageLiteral(resourceName: "BarBackground"),
                                                        for: .default)
navigationBar.shadowImage = UIImage()

Above is the only "official" way to hide it. Unfortunately, it removes bar's translucency.

I don't want background image, just color##

You have those options:

  1. Solid color, no translucency:

     navigationBar.barTintColor = UIColor.redColor()
     navigationBar.isTranslucent = false
     navigationBar.setBackgroundImage(UIImage(), for: .default)
     navigationBar.shadowImage = UIImage()
    
  2. Create small background image filled with color and use it.

  3. Use 'hacky' method described below. It will also keep bar translucent.

How to keep bar translucent?##

To keep translucency you need another approach, it looks like a hack but works well. The shadow we're trying to remove is a hairline UIImageView somewhere under UINavigationBar. We can find it and hide/show it when needed.

Instructions below assume you need hairline hidden only in one controller of your UINavigationController hierarchy.

  1. Declare instance variable:

    private var shadowImageView: UIImageView?
    
  2. Add method which finds this shadow (hairline) UIImageView:

    private func findShadowImage(under view: UIView) -> UIImageView? {
        if view is UIImageView && view.bounds.size.height <= 1 {
            return (view as! UIImageView)
        }
    
        for subview in view.subviews {
            if let imageView = findShadowImage(under: subview) {
                return imageView
            }
        }
        return nil
    }
    
  3. Add/edit viewWillAppear/viewWillDisappear methods:

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    
        if shadowImageView == nil {
            shadowImageView = findShadowImage(under: navigationController!.navigationBar)
        }
        shadowImageView?.isHidden = true
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
    
        shadowImageView?.isHidden = false
    }
    

The same method should also work for UISearchBar hairline, and (almost) anything else you need to hide :)

Many thanks to @Leo Natan for the original idea!

Solution 2

Here is the hack. Since it works on key paths might break in the future. But for now it works as expected.

Swift:

self.navigationController?.navigationBar.setValue(true, forKey: "hidesShadow")

Objective C:

[self.navigationController.navigationBar setValue:@(YES) forKeyPath:@"hidesShadow"];

Solution 3

If you just want to use a solid navigation bar color and have set this up in your storyboard, use this code in your AppDelegate class to remove the 1 pixel border via the appearance proxy:

[[UINavigationBar appearance] setBackgroundImage:[[UIImage alloc] init]
                                  forBarPosition:UIBarPositionAny
                                      barMetrics:UIBarMetricsDefault];

[[UINavigationBar appearance] setShadowImage:[[UIImage alloc] init]];

Solution 4

Try this:

[[UINavigationBar appearance] setBackgroundImage: [UIImage new]  
                                   forBarMetrics: UIBarMetricsDefault];

[UINavigationBar appearance].shadowImage = [UIImage new];

Below image has the explanation (iOS7 NavigationBar):

enter image description here

And check this SO question: iOS7 - Change UINavigationBar border color

Solution 5

The swift way to do it:

UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .any, barMetrics: .default)
UINavigationBar.appearance().shadowImage = UIImage()
Share:
212,218
Szymon Kuczur
Author by

Szymon Kuczur

Updated on February 02, 2022

Comments

  • Szymon Kuczur
    Szymon Kuczur over 2 years

    I have an app that sometimes needs its navigation bar to blend in with the content.

    Does anyone know how to get rid of or to change color of this annoying little bar?

    On the image below situation i have - i'm talking about this 1px height line below "Root View Controller"

    enter image description here

  • akashivskyy
    akashivskyy over 10 years
    As Serhii said, a custom background image has to be set for shadow image to be accepted.
  • SeanK
    SeanK over 10 years
    For keeping the bar translucent I needed to change view.bounds.size.height == 0.5 to view.bounds.size.height == 1.0 but other than that everything worked great. Thanks.
  • SeanK
    SeanK over 10 years
    An update to my earlier comment: the height needs to be 0.5 for retina devices but 1.0 for non-retina. I see you've change your condition to be <= 1.0 so that covers it.
  • cleverbit
    cleverbit over 9 years
    You can also just set the view of the UINavigationBar property: clipsToBounds = YES
  • Mahdi Yusuf
    Mahdi Yusuf over 9 years
    do this in your AppDelegate.
  • romeouald
    romeouald over 9 years
    @richarddas clipsToBounds = YES works like a charm! Thanks!
  • Christian Gossain
    Christian Gossain about 9 years
    The clipsToBounds doesn't always work for some layouts. This answer worked perfect for me. Only I created a UINavigationBar subclass and used the above code to hide the shadow image in the -layoutSubview methods. Thanks!
  • Jun
    Jun about 9 years
    The status bar area is also clipped.
  • Dănuț Mihai Florian
    Dănuț Mihai Florian almost 9 years
    Thanks James! I can't understand why someone down voted your answer.
  • Vlad
    Vlad almost 9 years
    Just to note I had problem where my iOS top status bar become translucent and I could see my table view scrolling behind the UINavigationBar. I fixed this by setting setTranslucent = NO.
  • sudo make install
    sudo make install almost 9 years
    It's pretty rare to get this far down the list before finding the sleekest answer. +1!
  • Axort
    Axort almost 9 years
    Hi guys! Do you know if this is 'App Store safe'? Thanks!
  • Alvivi
    Alvivi over 8 years
    @NunoGonçalves, you can think about flatAssociatedObjectKey as an unique int (treated as pointer) to identify your associated object. Here is defined by the memory address of a private var. I updated the response to add this var. See nshipster.com/associated-objects for more info.
  • user365314
    user365314 over 8 years
    It hides the whole background of the UINavigationBar tho :/
  • Akshay
    Akshay over 8 years
    Great answer... Saved my day _/\_
  • Alessandro Ornano
    Alessandro Ornano over 8 years
    if you need it, you can also adding: self.navigationController.navigationBar.translucent = false self.navigationController.navigationBar.clipsToBounds = false self.navigationController.navigationBar.shadowImage = UIImage() self.navigationController.navigationBar.backgroundColor = UIColor.blueColor()
  • ZYiOS
    ZYiOS over 8 years
    Good solution but works only when navigation bar is set translucent to false
  • user3344977
    user3344977 over 8 years
    This is the cleanest solution and best way to accomplish this. If you don't have to support < iOS 8, there's no reason not to use this answer. Thanks.
  • PeiweiChen
    PeiweiChen about 8 years
    Thank you! This one should be the answer.
  • vikzilla
    vikzilla about 8 years
    Works great. I set it in storyboard too so didn't even need any code.
  • TomSawyer
    TomSawyer about 8 years
    And it removes the background color also.
  • TomSawyer
    TomSawyer about 8 years
    Wrong, it hides the whole background too!
  • TomSawyer
    TomSawyer about 8 years
    It works with navigationBar but doesn't work with uitoolbar
  • Vegard
    Vegard about 8 years
    Doing this gives you a very tiny white line instead of the standard shadow.
  • Tommy K
    Tommy K almost 8 years
    Hi, new to iOS and trying to teach myself. How do I use the extension? I have a view controller file and I put these extensions in it outside of the class scope. But then how do I call hide/showHairline()? Don't really understand how to use extensions but its used for so many solutions, but I just don't understand how they are implemented in the actual code
  • tf.alves
    tf.alves almost 8 years
    The extensions adds functionality to your existing classes. Any extended class, structure, enum, etc, will have these new features available to you whenever you need to use them. See more here: developer.apple.com/library/ios/documentation/Swift/Conceptu‌​al/…
  • Tommy K
    Tommy K almost 8 years
    hm so I should just be able to use UINavigationController().navigationBar/toolbar.hide/showBott‌​omHairline() then no? I'm trying this but to no avail. Am I not understanding correctly?
  • JoeGalind
    JoeGalind almost 8 years
    +1 Of all the answers, this is what finally worked for me. It didn't mess up the status bar as other answers, and it addresses the problem of changing only one of the navigation controller bars, not all the navigation bars in the project.
  • Arpit B Parekh
    Arpit B Parekh over 7 years
    IIs it compulsory to have NavigationBarBackgound image ?
  • xi.lin
    xi.lin over 7 years
    In iOS 10 it seems that when viewWillAppear is called we cannot get shadowImageView
  • Vakas
    Vakas over 7 years
    I’m on Xcode 8 and developer for > iOS 8 and none of the above worked for me except this one.
  • Christoph
    Christoph over 7 years
    This sets it global for EVERY nav bar in your code base ... most of the times not what you want.
  • cloudcal
    cloudcal over 7 years
    Also on XCode 8 and > iOS 8. This is the only answer that worked for me.
  • Rohan Sanap
    Rohan Sanap over 7 years
    Dude!!! YOU ROCK!!! YOU ROCK!!! YOU ROCK!!! All cases perfectly answered and awesome solution to each case. This is the best answer. I personally encountered the situation of maintaining transparency! Thank you.
  • Akhrameev
    Akhrameev over 7 years
    @RPM Works great in iOS 7,8,9,10. There is the same code for viewControlles's navigation bar in medium for Swift.
  • dmathewwws
    dmathewwws over 7 years
    Note: you have to set the navigationBar's isTranslucent to false
  • Yongxiang Ruan
    Yongxiang Ruan about 7 years
    It is very helpful and complete. Thanks
  • badhanganesh
    badhanganesh about 7 years
    This one is perfect! Thanks for sharing. :)
  • Vishal Sonawane
    Vishal Sonawane almost 7 years
    This is the only short and simple answer which worked for me for iOS 10 and Xcode 8.3.1. Thanks man.
  • Marcelo dos Santos
    Marcelo dos Santos almost 7 years
    Objective-C: self.navigationController.navigationBar.shadowImage = [UIImage new];
  • jfredsilva
    jfredsilva over 6 years
    Objective-C: [self.navigationController.navigationBar setValue:@(YES) forKeyPath:@"hidesShadow"];
  • Vishnuvardhan
    Vishnuvardhan over 6 years
    @jfredsilva Thanks for the code. I will add this snippet for Objective C reference.
  • Hendrix
    Hendrix about 6 years
    This solved my problem for LargeTitles not working well with the previous solutions.
  • Vahid Amiri
    Vahid Amiri about 6 years
    Works just fine on iOS 11.
  • Cons Bulaquena
    Cons Bulaquena about 6 years
    Easily understandable to the nitty-gritty of UIs!
  • RichAppz
    RichAppz almost 6 years
    I can't believe this is still an issue - just came across it again! But this solution is the best possible one at the moment; Thanks @Socheat
  • Gandalf458
    Gandalf458 almost 6 years
    Swift 4 required: UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default) UINavigationBar.appearance().shadowImage = UIImage()
  • Gandalf458
    Gandalf458 almost 6 years
    Must be called in AppDelegate in didFinishLaunchingWithOptions
  • Chris Prince
    Chris Prince over 5 years
    I'm finding with iOS 12 this is not working any more. :(.
  • Shruti Thombre
    Shruti Thombre over 5 years
    This is exactly what i want, Thanks
  • akaralar
    akaralar over 5 years
    It can't find the hairline on iOS 12, is there a way to do that?
  • Prashant Tukadiya
    Prashant Tukadiya over 5 years
    Working perfectly !!
  • Jan Erik Schlorf
    Jan Erik Schlorf about 5 years
    clipsToBounds = YES will also clip the upper part of the navigation bar, exposing the background of the status bar, which can get problematic if your navigationBar has a custom color.
  • airowe
    airowe almost 5 years
    Swift 4.2 version: navigationBar?.setBackgroundImage(UIImage(), for: UIBarPosition.any, barMetrics: UIBarMetrics.default) navigationBar?.shadowImage = UIImage()
  • ttorbik
    ttorbik almost 5 years
    Works great iOS 13
  • Roger Oba
    Roger Oba over 4 years
    shadowImage and shadowColor aren't documented in UINavigationBarAppearance headers!! I'd never find it. Thanks, this solved my issues 100%
  • Yogesh Patel
    Yogesh Patel over 4 years
    Yes it's working fine for me too in iOS 13 thank you so much @glotcha.
  • glotcha
    glotcha over 4 years
    As of iOS 13 there is a system API to set or remove the shadow UIKit uses shadowImage and the shadowColor property to determine the shadow's appearance. When shadowImage is nil, the bar displays a default shadow tinted according to the value in the shadowColor property. If shadowColor is nil or contains the clearColor color, the bar displays no shadow. For code see my answer below.
  • Hitesh Surani
    Hitesh Surani over 4 years
    This one should be the answer. works like charm in ios 13
  • Argus
    Argus over 4 years
    .shadowColor solution: if #available(iOS 13.0, *) { let appearance = UINavigationBarAppearance() appearance.shadowColor = nil navigationController.navigationBar.standardAppearance = appearance }
  • Ravi
    Ravi about 4 years
    Only this working for me in iOS 13.3.1 Version iPad.
  • Roman Samoilenko
    Roman Samoilenko about 4 years
    What worked for me was navigationBar.standardAppearance.shadowColor = .clear
  • alekop
    alekop almost 4 years
    Setting scrollEdgeAppearance doesn't work on iOS 13.5. Setting clipToBounds = true does, but makes the background color slightly (but noticeably) different from the status bar color, even though I used the default color. Using standardAppearance does remove the separator, and preserves background color.
  • Theo Bendixson
    Theo Bendixson almost 4 years
    This isn't robust. Apple could stop using the "hidesShadow" keyPath at any moment, and your app will break as a result
  • yuzer
    yuzer over 3 years
    The only one that worked for me without any side effects. I'm using Opaque Navigation Bar.
  • andrewlundy
    andrewlundy over 3 years
    This is not a good answer. You're not directly accessing the API of a class, and this is subject to App Store rejection because the key path 'hidesShadow' could be removed at any point. If this happens, your app would immediately break. You can do this correctly in iOS 13 or later as followed: navigationBar.standardAppearance.shadowColor = nil
  • Jeffrey Neo
    Jeffrey Neo almost 3 years
    your answer will break the setup for isTranslucent = false, the background color will be changed. so far only the "to not use" answer is working for me, even in the latest iOS 14.4
  • Eric
    Eric over 2 years
    setting shadow image to empty worked for Swift5!!