535

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

Hexfire
  • 5,945
  • 8
  • 32
  • 42
Szymon Kuczur
  • 5,783
  • 3
  • 16
  • 14

49 Answers49

892

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!

Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278
Serhii Yakovenko
  • 12,424
  • 2
  • 27
  • 26
  • 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 Jan 02 '14 at 21:46
  • 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. – SeanK Jan 17 '14 at 22:30
  • 128
    You can also just set the view of the `UINavigationBar` property: `clipsToBounds = YES` – cleverbit Sep 06 '14 at 22:06
  • 5
    @richarddas `clipsToBounds = YES` works like a charm! Thanks! – romeouald Dec 12 '14 at 12:13
  • 3
    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! – Christian Gossain Feb 26 '15 at 17:34
  • 8
    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. – Vlad Jul 01 '15 at 06:04
  • Hi guys! Do you know if this is 'App Store safe'? Thanks! – Axort Aug 06 '15 at 20:56
  • IIs it compulsory to have NavigationBarBackgound image ? – Arpit B Parekh Aug 16 '16 at 11:52
  • 2
    In iOS 10 it seems that when `viewWillAppear ` is called we cannot get `shadowImageView` – xi.lin Aug 22 '16 at 04:15
  • It can't find the hairline on iOS 12, is there a way to do that? – akaralar Oct 24 '18 at 12:29
  • 2
    `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. – Jan Erik Schlorf Apr 25 '19 at 11:28
  • 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. – glotcha Oct 05 '19 at 03:26
  • 1
    .shadowColor solution: `if #available(iOS 13.0, *) { let appearance = UINavigationBarAppearance() appearance.shadowColor = nil navigationController.navigationBar.standardAppearance = appearance }` – Argus Nov 28 '19 at 09:12
  • 1
    What worked for me was `navigationBar.standardAppearance.shadowColor = .clear` – Roman Samoilenko Mar 25 '20 at 08:46
  • 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.** – alekop Jun 01 '20 at 23:15
272

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"];
Vishnuvardhan
  • 5,081
  • 1
  • 17
  • 33
147

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]];
Rick Pastoor
  • 3,625
  • 1
  • 21
  • 24
105

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

Community
  • 1
  • 1
Tarek Hallak
  • 18,422
  • 7
  • 59
  • 68
  • As Serhii said, a custom background image has to be set for shadow image to be accepted. – akashivskyy Oct 07 '13 at 14:31
  • 3
    do this in your AppDelegate. – Mahdi Yusuf Sep 25 '14 at 16:24
  • @RPM Works great in iOS 7,8,9,10. There is the same code for viewControlles's navigation bar in [medium](https://medium.com/simple-swift-programming-tips/how-to-disable-uinavigationbar-shadow-in-ios-9-swift-2bdf6e9be41e#.2gxthh3wr) for Swift. – Akhrameev Jan 10 '17 at 03:46
  • 1
    setting shadow image to empty worked for Swift5!! – Eric Oct 21 '21 at 08:11
71

The swift way to do it:

UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .any, barMetrics: .default)
UINavigationBar.appearance().shadowImage = UIImage()
Tung Fam
  • 7,899
  • 4
  • 56
  • 63
OscarVGG
  • 2,632
  • 2
  • 27
  • 34
65

Wanted to add the Swift version of Serhii's answer. I created a UIBarExtension.swift with the following:

import Foundation
import UIKit

extension UINavigationBar {
    func hideBottomHairline() {
        self.hairlineImageView?.isHidden = true
    }

    func showBottomHairline() {
        self.hairlineImageView?.isHidden = false
    }
}

extension UIToolbar {
    func hideBottomHairline() {
        self.hairlineImageView?.isHidden = true
    }

    func showBottomHairline() {
        self.hairlineImageView?.isHidden = false
    }
}

extension UIView {
    fileprivate var hairlineImageView: UIImageView? {
        return hairlineImageView(in: self)
    }

    fileprivate func hairlineImageView(in view: UIView) -> UIImageView? {
        if let imageView = view as? UIImageView, imageView.bounds.height <= 1.0 {
            return imageView
        }

        for subview in view.subviews {
            if let imageView = self.hairlineImageView(in: subview) { return imageView }
        }

        return nil
    }
}
Vishnuvardhan
  • 5,081
  • 1
  • 17
  • 33
pxpgraphics
  • 1,357
  • 11
  • 21
24

Simple solution in swift

   let navigationBar = self.navigationController?.navigationBar
    navigationBar?.setBackgroundImage(UIImage(), forBarPosition: UIBarPosition.Any, barMetrics: UIBarMetrics.Default)
    navigationBar?.shadowImage = UIImage()
Jakub Průša
  • 2,255
  • 1
  • 14
  • 19
  • 7
    +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. – JoeGalind Aug 03 '16 at 14:27
  • Swift 4.2 version: navigationBar?.setBackgroundImage(UIImage(), for: UIBarPosition.any, barMetrics: UIBarMetrics.default) navigationBar?.shadowImage = UIImage() – airowe Jul 19 '19 at 16:25
19

Can also be hidden from Storyboard (working on Xcode 10.1)

By adding runtime attribute: hidesShadow - Boolean - True

enter image description here

Gagandeep Gambhir
  • 4,225
  • 1
  • 29
  • 34
19

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.

    let appearance = UINavigationBarAppearance()
    appearance.shadowImage = nil
    appearance.shadowColor = nil
    navigationController.navigationBar.standardAppearance = appearance

https://developer.apple.com/documentation/uikit/uibarappearance/3198009-shadowimage

glotcha
  • 558
  • 6
  • 13
16

After studying the answer from Serhil, I created a pod UINavigationBar+Addition that can easily hide the hairline.

#import "UINavigationBar+Addition.h"

- (void)viewDidLoad {
    [super viewDidLoad];

    UINavigationBar *navigationBar = self.navigationController.navigationBar;
    [navigationBar hideBottomHairline];
}
samwize
  • 25,675
  • 15
  • 141
  • 186
16

In Swift 3.0

Edit your AppDelegate.swift by adding the following code to your application function:

// Override point for customization after application launch.

// Remove border in navigationBar
UINavigationBar.appearance().shadowImage = UIImage()
UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)
Vishnuvardhan
  • 5,081
  • 1
  • 17
  • 33
smohn
  • 401
  • 1
  • 5
  • 15
13

Swift 4 //for hiding navigation bar shadow line

navigationController?.navigationBar.shadowImage = UIImage()
Joe
  • 8,868
  • 8
  • 37
  • 59
Faris Muhammed
  • 970
  • 13
  • 17
11

pxpgraphics' solution updated for Swift 2.0

extension UINavigationBar {

    func hideBottomHairline()
    {
        hairlineImageViewInNavigationBar(self)?.hidden = true
    }

    func showBottomHairline()
    {
        hairlineImageViewInNavigationBar(self)?.hidden = false
    }

    private func hairlineImageViewInNavigationBar(view: UIView) -> UIImageView?
    {
        if let imageView = view as? UIImageView where imageView.bounds.height <= 1
        {
            return imageView
        }

        for subview: UIView in view.subviews
        {
            if let imageView = hairlineImageViewInNavigationBar(subview)
            {
                return imageView
            }
        }

        return nil
    }

}

extension UIToolbar
{

    func hideHairline()
    {
        let navigationBarImageView = hairlineImageViewInToolbar(self)?.hidden = true
    }

    func showHairline()
    {
        let navigationBarImageView = hairlineImageViewInToolbar(self)?.hidden = false
    }

    private func hairlineImageViewInToolbar(view: UIView) -> UIImageView?
    {
        if let imageView = view as? UIImageView where imageView.bounds.height <= 1
        {
            return imageView
        }

        for subview: UIView in view.subviews
        {
            if let imageView = hairlineImageViewInToolbar(subview)
            {
                return imageView
            }
        }

        return nil
    }

}
Community
  • 1
  • 1
tf.alves
  • 919
  • 8
  • 15
  • 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 – Tommy K Jun 01 '16 at 18:03
  • 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: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Extensions.html – tf.alves Jun 01 '16 at 18:11
  • hm so I should just be able to use `UINavigationController().navigationBar/toolbar.hide/showBottomHairline()` then no? I'm trying this but to no avail. Am I not understanding correctly? – Tommy K Jun 01 '16 at 20:26
11

Swift 4 Tested ONE LINE SOLUTION

In Viewdidload() Set Navigation controller's userdefault value true for key "hidesShadow"

override func viewDidLoad() {
    super.viewDidLoad()

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

}
Sachin Rasane
  • 1,501
  • 12
  • 17
10

I use a UINavigationBar extension that enables me to hide/show that shadow using the UIAppearance API or selecting which navigation bar has to hide/show that shadow using Storyboard (or source code). Here is the extension:

import UIKit

private var flatAssociatedObjectKey: UInt8 = 0

/*
  An extension that adds a "flat" field to UINavigationBar. This flag, when
  enabled, removes the shadow under the navigation bar.
 */
@IBDesignable extension UINavigationBar {
    @IBInspectable var flat: Bool {
        get {
            guard let obj = objc_getAssociatedObject(self, &flatAssociatedObjectKey) as? NSNumber else {
                return false
            }
            return obj.boolValue;
        }

        set {
            if (newValue) {
                let void = UIImage()
                setBackgroundImage(void, forBarPosition: .Any, barMetrics: .Default)
                shadowImage = void
            } else {
                setBackgroundImage(nil, forBarPosition: .Any, barMetrics: .Default)
                shadowImage = nil
            }
            objc_setAssociatedObject(self, &flatAssociatedObjectKey, NSNumber(bool: newValue),
                    objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}

Now, to disable the shadow across all navigation bars you have to use:

UINavigationBar.appearance().flat = true

Or you can enable/disable this behavior using storyboards:

Navigation Bar Storyboard

Alvivi
  • 3,263
  • 3
  • 26
  • 28
  • @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 http://nshipster.com/associated-objects/ for more info. – Alvivi Oct 29 '15 at 07:50
  • Good solution but works only when navigation bar is set `translucent` to false – ZYiOS Jan 18 '16 at 04:31
10

For iOS 13+

The trick is to initialize 'UINavigationBarAppearance' with TransparentBackground. Then you could easily remove the horizontal line of the navigation bar.

let appearance = UINavigationBarAppearance()
appearance.configureWithTransparentBackground()
appearance.backgroundColor = .green // Required background color

Finally, add the appearance changes to the navigation item as the apple suggested.

self.navigationItem.standardAppearance = appearance
self.navigationItem.scrollEdgeAppearance = appearance
self.navigationItem.compactAppearance = appearance
Mudith Chathuranga Silva
  • 7,253
  • 2
  • 50
  • 58
7

Another option if you want to preserve translucency and you don't want to subclass every UINavigationController in your app:

#import <objc/runtime.h>

@implementation UINavigationController (NoShadow)

+ (void)load {
    Method original = class_getInstanceMethod(self, @selector(viewWillAppear:));
    Method swizzled = class_getInstanceMethod(self, @selector(swizzled_viewWillAppear:));
    method_exchangeImplementations(original, swizzled);
}

+ (UIImageView *)findHairlineImageViewUnder:(UIView *)view {
    if ([view isKindOfClass:UIImageView.class] && view.bounds.size.height <= 1.0) {
        return (UIImageView *)view;
    }

    for (UIView *subview in view.subviews) {
        UIImageView *imageView = [self findHairlineImageViewUnder:subview];
        if (imageView) {
            return imageView;
        }
    }

    return nil;
}

- (void)swizzled_viewWillAppear:(BOOL)animated {
    UIImageView *shadow = [UINavigationController findHairlineImageViewUnder:self.navigationBar];
    shadow.hidden = YES;

    [self swizzled_viewWillAppear:animated];
}

@end
Vishnuvardhan
  • 5,081
  • 1
  • 17
  • 33
jhurliman
  • 1,790
  • 1
  • 18
  • 20
7

Swift put this

UINavigationBar.appearance().setBackgroundImage(UIImage(), forBarPosition: .Any, barMetrics: .Default)
UINavigationBar.appearance().shadowImage = UIImage()

in

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
UnRewa
  • 2,462
  • 2
  • 29
  • 31
6
Slightly Swift Solution 
func setGlobalAppearanceCharacteristics () {
    let navigationBarAppearace = UINavigationBar.appearance()
    navigationBarAppearace.tintColor = UIColor.white
    navigationBarAppearace.barTintColor = UIColor.blue
    navigationBarAppearace.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
    navigationBarAppearace.shadowImage = UIImage()

}
James
  • 71
  • 1
  • 5
  • Thanks James! I can't understand why someone down voted your answer. – Dănuț Mihai Florian Jun 07 '15 at 15:21
  • 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() – Alessandro Ornano Dec 16 '15 at 13:20
5

Two lines solution that works for me. Try to add this in ViewDidLoad method:

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

Here's a very simple solution:

self.navigationController.navigationBar.clipsToBounds = YES;
user3344977
  • 3,584
  • 4
  • 32
  • 88
4

In iOS8, if you set the UINavigationBar.barStyle to .Black you can set the bar's background as plain color without the border.

In Swift:

UINavigationBar.appearance().translucent = false
UINavigationBar.appearance().barStyle = UIBarStyle.Black
UINavigationBar.appearance().barTintColor = UIColor.redColor()
gpbl
  • 4,721
  • 5
  • 31
  • 45
4

Solution in Swift 4.2:

private func removeHairlineFromNavbar() {
    UINavigationBar.appearance().setBackgroundImage(
        UIImage(),
        for: .any,
        barMetrics: .default)
    UINavigationBar.appearance().shadowImage = UIImage()
}

Just put this function at the first Viewcontroller and call it in viewdidload

makle
  • 1,237
  • 1
  • 15
  • 21
4

Simple solution – Swift 5

  1. Create an extension:

    extension UIImage {
    
        class func hideNavBarLine(color: UIColor) -> UIImage? {
    
            let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
            UIGraphicsBeginImageContext(rect.size)
            let context = UIGraphicsGetCurrentContext()
            context?.setFillColor(color.cgColor)
            context?.fill(rect)
    
    
            let navBarLine = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return navBarLine
        }
    }
    
  2. Add this to viewDidLoad():

    self.navigationController?.navigationBar.shadowImage = UIImage.hideNavBarLine(color: UIColor.clear)
    
Diego Carrera
  • 2,245
  • 1
  • 13
  • 16
Bandyliuk
  • 489
  • 4
  • 13
2

The problem with setting a background image is it removes blurring. You can remove it without setting a background image. See my answer here.

Community
  • 1
  • 1
Léo Natan
  • 56,823
  • 9
  • 150
  • 195
2

For iOS 9 users, this worked for me. just add this:

UINavigationBar.appearance().shadowImage = UIImage()
2

pxpgraphics's answer for Swift 3.0.

import Foundation
import UIKit

extension UINavigationBar {

  func hideBottomHairline() {
    let navigationBarImageView = hairlineImageViewInNavigationBar(view: self)
    navigationBarImageView!.isHidden = true
  }

  func showBottomHairline() {
    let navigationBarImageView = hairlineImageViewInNavigationBar(view: self)
    navigationBarImageView!.isHidden = false
  }

  private func hairlineImageViewInNavigationBar(view: UIView) -> UIImageView? {
    if view is UIImageView && view.bounds.height <= 1.0 {
      return (view as! UIImageView)
    }

    let subviews = (view.subviews as [UIView])
    for subview: UIView in subviews {
      if let imageView: UIImageView = hairlineImageViewInNavigationBar(view: subview) {
        return imageView
      }
    }
    return nil
  }
}

extension UIToolbar {

  func hideHairline() {
    let navigationBarImageView = hairlineImageViewInToolbar(view: self)
    navigationBarImageView!.isHidden = true
  }

  func showHairline() {
    let navigationBarImageView = hairlineImageViewInToolbar(view: self)
    navigationBarImageView!.isHidden = false
  }

  private func hairlineImageViewInToolbar(view: UIView) -> UIImageView? {
    if view is UIImageView && view.bounds.height <= 1.0 {
      return (view as! UIImageView)
    }

    let subviews = (view.subviews as [UIView])
    for subview: UIView in subviews {
      if let imageView: UIImageView = hairlineImageViewInToolbar(view: subview) {
        return imageView
      }
    }
    return nil
  }
}
Reza Shirazian
  • 2,303
  • 1
  • 22
  • 30
2

You should add a view to a bottom of the UISearchBar

let rect = searchController.searchBar.frame;
let lineView : UIView = UIView.init(frame: CGRect.init(x: 0, y: rect.size.height-1, width: rect.size.width, height: 1))
lineView.backgroundColor = UIColor.init(hexString: "8CC73E")
searchController.searchBar.addSubview(lineView)
Socheat
  • 283
  • 1
  • 4
  • 11
  • 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 – RichAppz May 29 '18 at 12:14
1

I Just created an extension for this... Sorry about formatting (this is my first answer).

Usage:

  override func viewDidLoad() {
    super.viewDidLoad()
    self.navigationController?.hideShadow = true
}

Extension:

 UINavigationController.swift
//  Created by Ricardo López Rey on 16/7/15.

import Foundation


struct UINavigationControllerExtension {
    static var hideShadowKey : String = "HideShadow"
static let backColor = UIColor(red: 247/255, green: 247/255, blue: 248/255, alpha: 1.0)
}

extension UINavigationController {

    var hideShadow : Bool {
        get {
            if let ret =  objc_getAssociatedObject(self, &UINavigationControllerExtension.hideShadowKey) as? Bool {
                return ret
            } else {
                return false
            }


        }
        set {
            objc_setAssociatedObject(self,&UINavigationControllerExtension.hideShadowKey,newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC))

            if newValue {


            self.navigationBar.setBackgroundImage(solidImage(UINavigationControllerExtension.backColor), forBarMetrics: UIBarMetrics.Default)

                self.navigationBar.shadowImage = solidImage(UIColor.clearColor())
            } else {
                self.navigationBar.setBackgroundImage(nil, forBarMetrics: UIBarMetrics.Default)
            }
        }
    }

    private func solidImage(color: UIColor, size: CGSize = CGSize(width: 1,height: 1)) -> UIImage {
        var rect = CGRectMake(0, 0, size.width, size.height)
        UIGraphicsBeginImageContextWithOptions(size, false, 0)
        color.setFill()
        UIRectFill(rect)
        var image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }


}
Ricardo LR
  • 58
  • 1
  • 7
1
-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    UIImage *emptyImage = [UIImage new];
    self.navigationController.navigationBar.shadowImage = emptyImage;
    [self.navigationController.navigationBar setBackgroundImage:emptyImage forBarMetrics:UIBarMetricsDefault];
}
Axel Guilmin
  • 11,454
  • 9
  • 54
  • 64
Nike Kov
  • 12,630
  • 8
  • 75
  • 122
1

Within AppDelegate, this has globally changed the format of the NavBar:

 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

    UINavigationBar.appearance().setBackgroundImage(UIImage(), forBarPosition: UIBarPosition.Any, barMetrics: UIBarMetrics.Default)
    UINavigationBar.appearance().shadowImage = UIImage()
    UINavigationBar.appearance().tintColor = UIColor.whiteColor()
    UINavigationBar.appearance().barTintColor = UIColor.redColor()
    UINavigationBar.appearance().translucent = false
    UINavigationBar.appearance().clipsToBounds = false
    UINavigationBar.appearance().backgroundColor = UIColor.redColor()
    UINavigationBar.appearance().titleTextAttributes = [NSFontAttributeName : (UIFont(name: "FONT NAME", size: 18))!, NSForegroundColorAttributeName: UIColor.whiteColor()] }

Haven't managed to implement anything different on a specific VC, but this will help 90% of people

David West
  • 1,550
  • 1
  • 18
  • 31
1

Objective C Answer to Above Question

// removing 1px line of navigation bar

[[UINavigationBar appearance] setBackgroundImage:[[UIImage alloc]init] forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setShadowImage:[[UIImage alloc] init]];
[[UINavigationBar appearance] setTranslucent:NO];
[[UINavigationBar appearance] setTintColor:[UIColor yourColor]];
Vaibhav Gaikwad
  • 396
  • 5
  • 13
1

In Swift 3 we do this way

For any view controller:

navigationBar.shadowImage = UIImage()
setBackgroundImage(UIImage(), for: .default)

For an entire app:

UINavigationBar.appearance().setBackgroundImage(UIImage(),barMetrics: .Default)
UINavigationBar.appearance().shadowImage = UIImage()
Joe
  • 8,868
  • 8
  • 37
  • 59
Karthik damodara
  • 1,805
  • 19
  • 17
1

I ran into the same issue and none of the answers were truly satisfying. Here is my take for Swift3:

func hideNavigationBarLine() {
    navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
    navigationController?.navigationBar.shadowImage = UIImage()
}

Simply call this from within viewDidLoad().

easytarget
  • 749
  • 9
  • 10
1

Here is an way to do it without using any images, this is the only way that worked for me:

self.navigationController.navigationBar.layer.shadowOpacity = 0;

Unfortunately, you need to do this on every file where you want the line not to appear. There's no way to do it this way in appDelegate.

Edit:

Setting the shadowColor to nil isn't needed, this is the only line that you'll need.

Scott
  • 63
  • 10
1
        if #available(iOS 13.0, *) {
            let appearance = UINavigationBarAppearance()
            appearance.backgroundColor          = Colors.color_app
            appearance.titleTextAttributes      = [.foregroundColor : UIColor.white]
            appearance.largeTitleTextAttributes = [.foregroundColor : UIColor.white]
            appearance.shadowColor = .clear
            appearance.shadowImage = UIImage()
            
            UINavigationBar.appearance().tintColor            = .white
            UINavigationBar.appearance().standardAppearance   = appearance
            UINavigationBar.appearance().compactAppearance    = appearance
            UINavigationBar.appearance().scrollEdgeAppearance = appearance
        } else {
            UINavigationBar.appearance().barTintColor        = Colors.color_app
            UINavigationBar.appearance().tintColor           = .white
            UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.white]
            if #available(iOS 11.0, *) {
                UINavigationBar.appearance().largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
            }
            UINavigationBar.appearance().isTranslucent = false
            
            UINavigationBar.appearance().shadowImage = UIImage()
            UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)
        }
BatyrCan
  • 6,773
  • 2
  • 14
  • 23
1

It's very important to not use navigationController?.navigationBar.setValue(true, forKey: "hidesShadow") because at any time, Apple could remove the "hidesShadow" key path. If they were to do this, any app using this call would break. Since you are not accessing the direct API of a class, this call is subject to App Store rejection.

As of iOS 13, to ensure efficiency, you can do the following:

navigationBar.standardAppearance.shadowColor = nil

andrewlundy
  • 1,073
  • 15
  • 22
  • 1
    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 – Jeffrey Neo Jun 21 '21 at 10:24
1

One very important note here - it's a lot more flexible to change the appearance of the UIViewController's navigationItem than the navigationBar directly.

Why you ask?

For the simple reason that the navigationItem is tied to a single UIViewController and represents the state of the navigationBar for that particular UIViewController. This is big, as you don't have to handle the navigation bar changes between different view controllers within viewWillAppear (or something similar), as you would if you mutated the navigationBar; which is, remember, shared between all view controllers of a given navigation stack (UINavigationController), and changing it in one place changes it for all view controllers up to the stack.

You just set the correct UINavigationBarAppearance for a particular view controller and UIKit will correctly update the navigation bar styling depeneding on which view controller is currently the top view controller on the navigation stack.

navigationItem.standardAppearance` = `UINavigationBarAppearance()
D6mi
  • 611
  • 7
  • 16
0

This might sound stupid, but this hairline only appears when the background color for viewController's view is set to any color, but white. I was shocked to learn this fact.

So if you want it to disappear without much trouble just set the controller's view background color to WHITE COLOR.

bummi
  • 27,123
  • 14
  • 62
  • 101
Pavel Gurov
  • 5,587
  • 3
  • 26
  • 23
0

I know this is an old thread, but I found a solution that works really well:

Subclass UINavigationBar. In your UINavigationBar subclass, override didAddSubview with the following code:

- (void)didAddSubview:(UIView *)subview
{
    [super didAddSubview:subview];

    if ([subview isKindOfClass:[UIImageView class]]) {
        [subview setClipsToBounds:YES];
    }
}
0
[tabviewController.view setBackgroundColor:[UIColor blackColor]];

Did it for me [UIColor blackColor] might be your background color, and tabviewController is your UITabBarController if you are using it!

slavoo
  • 5,798
  • 64
  • 37
  • 39
Jeffrey Snijder
  • 111
  • 1
  • 3
0

My approach:

UINavigationBar.appearance().setBackgroundImage(
            UIImage(),
            forBarPosition: .Any,
            barMetrics: .Default)
    var _width:CGFloat! = self.navigationController?.navigationBar.layer.frame.width
            var _height:CGFloat! = self.navigationController?.navigationBar.layer.frame.height
            var navBarBg = UIView(frame:CGRectMake(0, 0, _width, _height))
            //solid color for bg
            navBarBg.backgroundColor = UIColor.orangeColor()
            view.addSubview(navBarBg)
Maysam
  • 7,246
  • 13
  • 68
  • 106
0

Here's another option - I think this only works if you don't require translucency on your nav bar (I didn't). I just added a 1 pixel high UIView to the bottom of the nav bar (1 pixel below the nav bar) with the same colour as my nav bar:

UIView *view = [[UIView alloc] init];
[view setBackgroundColor:self.navigationController.navigationBar.barTintColor];
[self.navigationController.navigationBar addSubview:view];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
    make.height.equalTo(@(1.0f));
    make.leading.trailing.equalTo(self.navigationController.navigationBar);
    make.bottom.equalTo(self.navigationController.navigationBar).offset(1.0f);
}];

I'm adding the constraints using Masonry.

jwswart
  • 1,226
  • 14
  • 16
0

Bar style black did it for me.

[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];

All properties that I have (just in case):

    [[UINavigationBar appearance] setBarTintColor:color];
    [[UINavigationBar appearance] setTranslucent:NO];
    [[UINavigationBar appearance] setShadowImage:[UIImage new]];
    [[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];
user1122069
  • 1,767
  • 1
  • 24
  • 52
0

What worked for me, and was simplest, was to create a png (it only needs to be one pixel by one pixel in dimensions) with the required color and then set the backgroundImage and shadowImage to that:

let greenPixel = UIImage(named: "TheNameOfYourPng")
navigationBar.setBackgroundImage(greenPixel, forBarMetrics: UIBarMetrics.Default)
navigationBar.shadowImage = greenPixel
Gruntcakes
  • 37,738
  • 44
  • 184
  • 378
0

A nice short Swift function to find the hairline in the subviews is this one:

 func findHairLineInImageViewUnder(view view: UIView) -> UIImageView? {
    if let hairLineView = view as? UIImageView where hairLineView.bounds.size.height <= 1.0 {
        return hairLineView
    }

    if let hairLineView = view.subviews.flatMap({self.findHairLineInImageViewUnder(view: $0)}).first {
      return hairLineView
    }

    return nil
}
Marijn
  • 1,439
  • 12
  • 12
0

In Xamarin Forms this worked for me. Just add this on AppDelegate.cs:

UINavigationBar.Appearance.ShadowImage = new UIImage();
0

Hi this works for Swift 4.

    override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    self.navigationController?.navigationBar.shadowImage = UIImage()
    self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
    self.navigationController?.navigationBar.isTranslucent = false
}

you need to put this in viewDidLayoutSubviews instead of viewDidLoad

Kelvin Fok
  • 621
  • 7
  • 9
0

Write your own initializer :D

import Foundation
import UIKit

extension UINavigationController {
    convenience init(rootViewController : UIViewController, hidesShadow : Bool) {
        self.init(rootViewController : rootViewController)
        self.navigationBar.setValue(hidesShadow, forKey: "hidesShadow")
        if hidesShadow {
            self.extendedLayoutIncludesOpaqueBars = true
            self.navigationBar.isTranslucent = false 
        }
    }
}
Ankur Lahiry
  • 2,253
  • 1
  • 15
  • 25