157

i've been trying to remove the navigationBars border without luck. I've researched and people seem to tell to set shadowImage and BackgroundImage to nil, but this does not work in my case.

My code

    self.navigationController?.navigationBar.barTintColor = UIColor(rgba: "#4a5866")
    self.navigationController?.navigationBar.setBackgroundImage(UIImage(named: ""), forBarMetrics: UIBarMetrics.Default)
    self.navigationController?.navigationBar.shadowImage = UIImage(named: "")

illustration:

enter image description here

Sazzad Hissain Khan
  • 37,929
  • 33
  • 189
  • 256
Peter Pik
  • 11,023
  • 19
  • 84
  • 142

29 Answers29

360

The trouble is with these two lines:

self.navigationController?.navigationBar.setBackgroundImage(UIImage(named: ""), forBarMetrics: UIBarMetrics.Default)
self.navigationController?.navigationBar.shadowImage = UIImage(named: "")

Since you don't have an image with no name, UIImage(named: "") returns nil, which means the default behavior kicks in:

When non-nil, a custom shadow image to show instead of the default shadow image. For a custom shadow to be shown, a custom background image must also be set with -setBackgroundImage:forBarMetrics: (if the default background image is used, the default shadow image will be used).

You need a truly empty image, so just initialize with UIImage():

self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
self.navigationController?.navigationBar.shadowImage = UIImage()
rsc
  • 10,348
  • 5
  • 39
  • 36
Nate Cook
  • 92,417
  • 32
  • 217
  • 178
73

Swift 4 & Swift 5

Removing border:

self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for:.default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.layoutIfNeeded()

Restoring border:

self.navigationController?.navigationBar.setBackgroundImage(nil, for:.default)
self.navigationController?.navigationBar.shadowImage = nil
self.navigationController?.navigationBar.layoutIfNeeded()
Sazzad Hissain Khan
  • 37,929
  • 33
  • 189
  • 256
42

With Swift 2 you can do it this way:

AppDelegate file

Inside func application(..., didFinishLaunchingWithOptions launchOptions:...)

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

for Swift 3:

UINavigationBar.appearance().shadowImage = UIImage()
UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)
ayalcinkaya
  • 3,303
  • 29
  • 25
Jorge Casariego
  • 21,948
  • 6
  • 90
  • 97
40

Just write this in the extension of UINavigationBar

extension UINavigationBar {

    func shouldRemoveShadow(_ value: Bool) -> Void {
        if value {
            self.setValue(true, forKey: "hidesShadow")
        } else {
            self.setValue(false, forKey: "hidesShadow")
        }
    }
}

And in your viewController...

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.navigationController?.navigationBar.shouldRemoveShadow(true)        
}

And to get this undone for any viewController, just pass false.

Gaurav Chandarana
  • 738
  • 10
  • 10
  • This works great BUT it hides if for ALL vcs on the nav stack. If you use it in vc1 and vc1 can push to vc2 then it will be hidden on vc2 also. To show the shadow in vc2 you will have to set it to false in viewDidLoad. The problem is when you pop back to vc1 it'll show again because it was reset in vc2. You have to use logic to go back and forth which might not be worth it. However if you don't want the shadow image to show on any vcs at all then this is the easiest way to go – Lance Samaria Sep 18 '17 at 18:34
  • Let's say we have a stack of viewControllers with 5 viewControllers(And we've hidden the shadow in the first one). Now for the 3rd viewController only, I we DON"T want to hide the shadow. So, I'll call that method with FALSE in viewWillAppear and with TRUE in viewWillDisappear of 3rd viewController. That's all it takes! – Gaurav Chandarana Sep 19 '17 at 06:06
  • your absolutely correct, good thinking! Don't think I knocked your answer because it's very succinct and efficient, I also up voted it. I use it to remove it & the navBar for error messages. The issue I found was when removing it in vc1 but showing it vc2 but if there was an error in vc2 then removing it in viewWillDisappear might not work. But again it's a very unique situation. I do like the viewWillDisappear idea for general case use and you should add it to your answer. Regardless your code works and it's an easy way to remove the shadow! – Lance Samaria Sep 19 '17 at 12:40
  • Works like a charm. Just wanted to make sure this isn't private settings? – inokey Oct 19 '17 at 09:35
  • @inokey it is private, from my understanding. – nmdias Oct 20 '17 at 19:42
  • @nmdias will it cause any problems with uploading to AppStore in that case? Have you tried it? – inokey Oct 23 '17 at 07:13
  • @inokey I have no ideia. 'm gonna find out in a month or so when I upload an App with it. – nmdias Oct 23 '17 at 15:04
  • @inokey, it won't cause any problems, I've uploaded a build on TestFlight with this. – Gaurav Chandarana Oct 24 '17 at 05:35
  • 2
    This is an excellent answer! I've added an answer that streamlines the code. – Scott Gardner Nov 24 '18 at 14:39
  • This is answer is not working anymore in Swift 5.8 :/ – Creeky Crap Jun 23 '23 at 13:32
38

this will remove the shadow image altogether

for parent in self.navigationController!.navigationBar.subviews {
 for childView in parent.subviews {
     if(childView is UIImageView) {
         childView.removeFromSuperview()
     }
 }
}
Luca Davanzo
  • 21,000
  • 15
  • 120
  • 146
Steve Payne
  • 493
  • 4
  • 7
31

Swift 5

When using setBackgroundImage / shadowImage to hide the hairline, there's a slight delay. This method removes the delay. Credit to Chameleon Framework. This is the method they use (in ObjC)


extension UINavigationController {
    func hideHairline() {
        if let hairline = findHairlineImageViewUnder(navigationBar) {
            hairline.isHidden = true
        }
    }
    func restoreHairline() {
        if let hairline = findHairlineImageViewUnder(navigationBar) {
            hairline.isHidden = false
        }
    }
    func findHairlineImageViewUnder(_ view: UIView) -> UIImageView? {
        if view is UIImageView && view.bounds.size.height <= 1.0 {
            return view as? UIImageView
        }
        for subview in view.subviews {
            if let imageView = self.findHairlineImageViewUnder(subview) {
                return imageView
            }
        }
        return nil
    }
}

Jack Chen
  • 488
  • 5
  • 7
29
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithTransparentBackground()
GIJoeCodes
  • 1,680
  • 1
  • 25
  • 28
14

Set barStyle to .Black before setting the tint:

self.navigationController?.navigationBar.translucent = false
self.navigationController?.navigationBar.barStyle = .Black
self.navigationController?.navigationBar.barTintColor = UIColor.blueColor()
gpbl
  • 4,721
  • 5
  • 31
  • 45
  • is it kinda random that this actually works? or am i overthinking? – joe Jul 29 '15 at 18:54
  • @joe well it is working here :-) does it not work for you? – gpbl Jul 30 '15 at 07:16
  • that's the thing, it DOES work. i'm just wondering if there is an explanation as to why turning the barStyle to black it then turns the whole barTintColor to blue :) – joe Jul 30 '15 at 18:36
  • maybe setting it to black and opaque, it switches off some layers/view in the navigationBar... – gpbl Jul 31 '15 at 13:44
  • I tried using this, and it does remove the bottom line however if using a bar tint of white the title is not visible :/ – RileyDev Aug 06 '15 at 23:50
  • This works. All answers did not do it for me. I am using iOS 11 swift 4.1. Thanks man. – 726a May 21 '18 at 03:38
  • @RileyDev I just tried it and you have to set tintColor separately for that. – iprateekk Jun 01 '18 at 16:15
14

For iOS 13+:

let appearance = UINavigationBarAppearance()
appearance.shadowColor = .clear

Assign this appearance to the UINavigationBar:

navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance = appearance
navigationController?.navigationBar.compactAppearance = appearance

Setting shadowImage = UIImage() didn't work for me.

Drew
  • 674
  • 5
  • 14
10

Luca Davanzo's answer is great, but it does not work in iOS 10. I altered it to work in iOS 10 and below.

for parent in navigationController!.view.subviews {
    for child in parent.subviews {
        for view in child.subviews { 
            if view is UIImageView && view.frame.height == 0.5 {
                view.alpha = 0
            }
        }
    }
}

You can also extend UINavigationController and call this off of that. removeFromSuperview() on the line will not work on iOS 10, so I just set the alpha to 0 so this one call is compatible everywhere.

David Baez
  • 1,208
  • 1
  • 14
  • 26
  • Nice catch, any way to show/hide the shadow in some viewControllers while navigating? – Ashkan.H Jan 19 '17 at 16:56
  • You should rather check for heights that have a `height >= 1.0`. On iPhone models that have a 3x retina screen (e.g. 8 Plus, XR...) the hairline has a height of 0.33. – heyfrank Mar 13 '19 at 10:30
9

To remove border from UINavigationBar in Swift 3+, use:

UINavigationBar.appearance().shadowImage = UIImage()
UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)
UINavigationBar.appearance().isTranslucent = false
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
reza_khalafi
  • 6,230
  • 7
  • 56
  • 82
9

Swiftier method of Jack Chen:

extension UINavigationController {

    var isHiddenHairline: Bool {
        get {
            guard let hairline = findHairlineImageViewUnder(navigationBar) else { return true }
            return hairline.isHidden
        }
        set {
            if let hairline = findHairlineImageViewUnder(navigationBar) {
                hairline.isHidden = newValue
            }
        }
    }

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

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

        return nil
    }
}

Using:

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        navigationController?.isHiddenHairline = true
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        navigationController?.isHiddenHairline = false
    }
Mickael Belhassen
  • 2,970
  • 1
  • 25
  • 46
8

Only this worked for me,

self.navigationController?.navigationBar.shadowImage = UIImage()

Ref

Mohammad Zaid Pathan
  • 16,304
  • 7
  • 99
  • 130
7

for swift 3

in viewDidLoad method

navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationController?.navigationBar.shadowImage = UIImage()
Twitter khuong291
  • 11,328
  • 15
  • 80
  • 116
Gulz
  • 1,773
  • 19
  • 15
6

Updated for Swift 4 in case someone is wondering

navigationBar.shadowImage = UIImage()
navigationBar.backIndicatorImage = UIImage()

It's even less verbose now.

Mauricio Chirino
  • 1,182
  • 17
  • 19
3

The accepted answer worked for me but I noticed when I wanted the shadow image to reappear when popping back or pushing forward to another vc there was a noticeable blink in the navigation bar.

Using this method navigationController?.navigationBar.setValue(true, forKey: "hidesShadow") in viewWillAppear the shadow bar is hidden in the current visible view controller.

Using these 2 methods

navigationController?.navigationBar.setBackgroundImage(nil, for: .default)
navigationController?.navigationBar.setValue(false, forKey: "hidesShadow")

in viewWillDisappear the blink still happens but only when the shadow image reappears and not the navigation bar itself.

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    // 1. hide the shadow image in the current view controller you want it hidden in
    navigationController?.navigationBar.setValue(true, forKey: "hidesShadow")
    navigationController?.navigationBar.layoutIfNeeded()
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(true)

    // 2. show the shadow image when pushing or popping in the next view controller. Only the shadow image will blink
    navigationController?.navigationBar.setBackgroundImage(nil, for: .default)
    navigationController?.navigationBar.setValue(false, forKey: "hidesShadow")
    navigationController?.navigationBar.layoutIfNeeded()
}
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
3

If you want to remove only the bottom line and keep the solid color of navigationBar, add these lines of code in viewDidLoad: Swift 3, 4:

navigationController?.navigationBar.shadowImage = UIImage()
navigationController?.navigationBar.isTranslucent = false

Peace!

Mihail Salari
  • 1,471
  • 16
  • 17
2

This is the way if you want to do it without changing the background color:

// Remove the border ImageView from the NavigationBar background
func hideBottomBorder() {
    for view in navigationBar.subviews.filter({ NSStringFromClass($0.dynamicType) == "_UINavigationBarBackground" }) as [UIView] {
        if let imageView = view.subviews.filter({ $0 is UIImageView }).first as? UIImageView {
            imageView.removeFromSuperview()
        }
    }
}

NOTE: This might crash on a production app. Apparently the NavigationBar doesn't like its view disappearing

Yariv Nissim
  • 13,273
  • 1
  • 38
  • 44
2

Within AppDelegate, this has globally changed the format of the NavBar and removes the bottom line/border:

 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
2

in your custom navigationController add these lines:

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

Important Note

the last line is important if you use the first line viewDidLoad() method because navigationController should redraw nav bar but easily you can use this without layoutIfNeeded() in the viewWillAppear() method before it draws the nav bar

Mr Zee
  • 87
  • 7
1

for the swift3 you should write slightly different way:

 self.navigationController?.navigationBar.setBackgroundImage(UIImage(),
    for: UIBarMetrics.default)
   self.navigationController?.navigationBar.shadowImage = UIImage()
Chetan Dobariya
  • 792
  • 8
  • 14
1

This is a streamlined version of Gaurav Chandarana's answer.

extension UINavigationBar {

    func hideShadow(_ value: Bool = true) {
        setValue(value, forKey: "hidesShadow")
    }
}
Scott Gardner
  • 8,603
  • 1
  • 44
  • 36
1

App delegate

UINavigationBar.appearance().setBackgroundImage(UIImage(), for: UIBarMetrics.default)
UINavigationBar.appearance().shadowImage = UIImage()
Srinivasan_iOS
  • 972
  • 10
  • 12
1

I use this code in AppDelegate's didFinishLaunchingWithOptions method to reach it in whole app:

 let barAppearance = UINavigationBar.appearance()
    
 if #available(iOS 13, *) {
     let appearance = UINavigationBarAppearance()
     appearance.configureWithTransparentBackground()
     barAppearance.standardAppearance = appearance
     barAppearance.scrollEdgeAppearance = appearance
 } else {
     barAppearance.setBackgroundImage(UIImage(), for: UIBarPosition.any, barMetrics: UIBarMetrics.defaultPrompt)
     barAppearance.shadowImage = UIImage()
 }
0

The border line is an UIImageView and removing a subview which is an imageView will remove barButtonItems with UIImageView. Below code will help you remove it. Hope this helps someone who faced an issue like me.

for parent in self.navigationController!.navigationBar.subviews {
        for childView in parent.subviews {
            if childView.frame.height == 0.5 {
                childView.removeFromSuperview()
            }
        }
    }

The border UIImageView is only 0.5 in height so this code removes only that.

bachman
  • 690
  • 7
  • 22
  • This caused crashes for me. I think parent and childViews need to be unwrapped in case they're nil before checking each one. I am using a custom UINavigationController though, so this might not be the case for other users with a standard bar. – Natalia Aug 19 '16 at 23:46
0

this is the answer in swift 3 base of Nate Cook answer

   self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
    self.navigationController?.navigationBar.shadowImage = UIImage()
Alan
  • 329
  • 4
  • 10
0

iOS 11 and Swift 4 You should try following if you want to remove the border but don't to make the navigitonbar translucent
self.navigationBar.shadowImage = UIImage()

BilalReffas
  • 8,132
  • 4
  • 50
  • 71
0

I am using Xcode 14.2. If you want to do this using Xcode UI, then select the Navigation controller, then Navigation Bar, then Attributes Inspector and Under Scroll Edge Appearance, set the Shadow Color to clear color.

enter image description here

-1

I found changing the navigation bar shadow color to Clear from the activity inspector to solve this issue

[changing the navigation bar shadow color****strong text][1]

[1]: https://i.stack.imgur.com/i3Kdo.png***strong text***