How to hide UINavigationBar 1px bottom line
Asked Answered
R

50

537

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

Ronnyronsard answered 7/10, 2013 at 14:4 Comment(2)
review this : #19101861Futile
how to increase 1px of navigation height?Shakeup
J
903

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!

Joplin answered 7/10, 2013 at 14:14 Comment(15)
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.Unyielding
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.Unyielding
You can also just set the view of the UINavigationBar property: clipsToBounds = YESSquawk
@richarddas clipsToBounds = YES works like a charm! Thanks!Anoint
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!Recess
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.Brimmer
Hi guys! Do you know if this is 'App Store safe'? Thanks!Gifferd
IIs it compulsory to have NavigationBarBackgound image ?Masteratarms
In iOS 10 it seems that when viewWillAppear is called we cannot get shadowImageViewAdapa
It can't find the hairline on iOS 12, is there a way to do that?Lye
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.Abba
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.Olympus
.shadowColor solution: if #available(iOS 13.0, *) { let appearance = UINavigationBarAppearance() appearance.shadowColor = nil navigationController.navigationBar.standardAppearance = appearance }Thermae
What worked for me was navigationBar.standardAppearance.shadowColor = .clearLolly
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.Gamble
G
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"];
Gusgusba answered 3/8, 2016 at 13:43 Comment(11)
I’m on Xcode 8 and developer for > iOS 8 and none of the above worked for me except this one.Interstate
Also on XCode 8 and > iOS 8. This is the only answer that worked for me.Bode
This is the only short and simple answer which worked for me for iOS 10 and Xcode 8.3.1. Thanks man.Essie
Objective-C: self.navigationController.navigationBar.shadowImage = [UIImage new];Drava
Objective-C: [self.navigationController.navigationBar setValue:@(YES) forKeyPath:@"hidesShadow"];Burned
@Burned Thanks for the code. I will add this snippet for Objective C reference.Gusgusba
This solved my problem for LargeTitles not working well with the previous solutions.Atonic
Works just fine on iOS 11.Wop
Only this working for me in iOS 13.3.1 Version iPad.Fluctuation
This isn't robust. Apple could stop using the "hidesShadow" keyPath at any moment, and your app will break as a resultAgainst
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 = nilSides
S
148

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]];
Sixtieth answered 27/10, 2013 at 9:48 Comment(1)
This sets it global for EVERY nav bar in your code base ... most of the times not what you want.Fluoroscopy
L
106

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

Lordan answered 7/10, 2013 at 14:6 Comment(4)
As Serhii said, a custom background image has to be set for shadow image to be accepted.Sisson
do this in your AppDelegate.Quinonoid
@RPM Works great in iOS 7,8,9,10. There is the same code for viewControlles's navigation bar in medium for Swift.Department
setting shadow image to empty worked for Swift5!!Eellike
M
72

The swift way to do it:

UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .any, barMetrics: .default)
UINavigationBar.appearance().shadowImage = UIImage()
Minimal answered 10/4, 2015 at 16:8 Comment(5)
It's pretty rare to get this far down the list before finding the sleekest answer. +1!Fromenty
It hides the whole background of the UINavigationBar tho :/Helmuth
Wrong, it hides the whole background too!Heterodoxy
Note: you have to set the navigationBar's isTranslucent to falseHeliogravure
The only one that worked for me without any side effects. I'm using Opaque Navigation Bar.Carpenter
I
66

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
    }
}
Incisive answered 26/7, 2014 at 19:44 Comment(5)
Thank you! This one should be the answer.Wieland
It works with navigationBar but doesn't work with uitoolbarHeterodoxy
I'm finding with iOS 12 this is not working any more. :(.Norean
Works great iOS 13Broeker
This one should be the answer. works like charm in ios 13Caffey
S
25

Simple solution in swift

   let navigationBar = self.navigationController?.navigationBar
    navigationBar?.setBackgroundImage(UIImage(), forBarPosition: UIBarPosition.Any, barMetrics: UIBarMetrics.Default)
    navigationBar?.shadowImage = UIImage()
Straley answered 2/11, 2015 at 12:1 Comment(2)
+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.Isochronous
Swift 4.2 version: navigationBar?.setBackgroundImage(UIImage(), for: UIBarPosition.any, barMetrics: UIBarMetrics.default) navigationBar?.shadowImage = UIImage()Swallowtailed
E
20

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

By adding runtime attribute: hidesShadow - Boolean - True

enter image description here

Extern answered 11/1, 2019 at 1:8 Comment(0)
O
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

Olympus answered 23/6, 2019 at 8:1 Comment(2)
shadowImage and shadowColor aren't documented in UINavigationBarAppearance headers!! I'd never find it. Thanks, this solved my issues 100%Copeland
Yes it's working fine for me too in iOS 13 thank you so much @glotcha.Command
W
17

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];
}
Weather answered 14/3, 2014 at 7:13 Comment(0)
D
17

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)
Dinesh answered 11/2, 2017 at 20:16 Comment(0)
O
14

Swift 4 //for hiding navigation bar shadow line

navigationController?.navigationBar.shadowImage = UIImage()
Oaten answered 4/12, 2017 at 5:12 Comment(0)
P
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
    }

}
Pitarys answered 13/7, 2015 at 10:50 Comment(3)
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 codeAmbie
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/Conceptual/…Pitarys
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?Ambie
N
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")

}
Niven answered 15/11, 2018 at 6:34 Comment(1)
Working perfectly !!Decimalize
A
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

Anking answered 4/8, 2015 at 21:10 Comment(2)
@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.Anking
Good solution but works only when navigation bar is set translucent to falseHakon
N
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
Nozicka answered 13/12, 2021 at 11:25 Comment(0)
H
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
Hortatory answered 5/12, 2014 at 6:44 Comment(0)
V
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
Vaccine answered 2/2, 2016 at 10:44 Comment(1)
And it removes the background color also.Heterodoxy
M
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()

}
Meingoldas answered 1/4, 2015 at 17:12 Comment(2)
Thanks James! I can't understand why someone down voted your answer.Maestas
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()Chrischrism
R
5

Here's a very simple solution:

self.navigationController.navigationBar.clipsToBounds = YES;
Rebbecarebbecca answered 6/2, 2015 at 9:39 Comment(1)
The status bar area is also clipped.Hayott
Z
5

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

navigationController?.navigationBar.setValue(true, forKey: "hidesShadow")
self.extendedLayoutIncludesOpaqueBars = true
Zingale answered 11/7, 2019 at 9:4 Comment(0)
M
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()
Milson answered 20/7, 2015 at 17:19 Comment(3)
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.Rebbecarebbecca
Works great. I set it in storyboard too so didn't even need any code.Chiba
Doing this gives you a very tiny white line instead of the standard shadow.Flip
M
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

Meggie answered 24/2, 2019 at 14:4 Comment(0)
H
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)
    
Handmaiden answered 10/7, 2019 at 11:51 Comment(0)
I
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.

Ilowell answered 27/10, 2013 at 10:6 Comment(0)
P
2

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

UINavigationBar.appearance().shadowImage = UIImage()
Pantagruel answered 1/7, 2016 at 17:25 Comment(0)
I
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
  }
}
Inspectorate answered 17/11, 2016 at 22:26 Comment(0)
C
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)
Comptom answered 10/1, 2017 at 2:32 Comment(1)
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 @ComptomHuerta
C
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
    }


}
Crotty answered 16/7, 2015 at 17:56 Comment(0)
P
1
-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    UIImage *emptyImage = [UIImage new];
    self.navigationController.navigationBar.shadowImage = emptyImage;
    [self.navigationController.navigationBar setBackgroundImage:emptyImage forBarMetrics:UIBarMetricsDefault];
}
Pompano answered 11/5, 2016 at 14:21 Comment(0)
F
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

Filmer answered 20/5, 2016 at 15:21 Comment(0)
M
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]];
Magnificent answered 5/11, 2016 at 10:35 Comment(0)
A
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()
Argyrol answered 2/6, 2017 at 10:51 Comment(2)
Swift 4 required: UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default) UINavigationBar.appearance().shadowImage = UIImage()Occidentalize
Must be called in AppDelegate in didFinishLaunchingWithOptionsOccidentalize
B
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().

Beilul answered 24/8, 2017 at 20:43 Comment(0)
F
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.

Furthermost answered 15/8, 2018 at 22:3 Comment(0)
S
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)
        }
Secrecy answered 12/7, 2020 at 23:35 Comment(0)
S
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

Sides answered 23/11, 2020 at 6:39 Comment(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.4Pampuch
B
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()
Bruxelles answered 3/6, 2021 at 11:3 Comment(0)
A
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.

Alage answered 19/2, 2014 at 18:58 Comment(0)
D
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];
    }
}
Descendant answered 21/4, 2014 at 22:6 Comment(0)
I
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!

Ics answered 5/1, 2015 at 13:2 Comment(0)
H
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)
Hydrogenous answered 10/6, 2015 at 6:55 Comment(0)
P
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.

Pup answered 28/9, 2015 at 18:25 Comment(0)
W
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];
Wera answered 10/12, 2015 at 23:10 Comment(0)
B
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
Babbie answered 8/6, 2016 at 18:44 Comment(0)
D
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
}
Dink answered 30/6, 2016 at 12:16 Comment(0)
T
0

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

UINavigationBar.Appearance.ShadowImage = new UIImage();
Transudation answered 3/11, 2017 at 22:58 Comment(0)
D
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

Dermato answered 5/12, 2017 at 3:38 Comment(0)
A
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 
        }
    }
}
Aikoail answered 13/10, 2020 at 6:27 Comment(0)
N
0

Combination of answers got me to what I am aiming for.

I used this:

let navigationBarAppearance = UINavigationBarAppearance()
navigationBarAppearance.configureWithOpaqueBackground()
navigationBarAppearance.shadowColor = .clear
navigationItem.scrollEdgeAppearance = navigationBarAppearance

Thin bar is still visible when I only use .configureWithOpaqueBackground(). The whole navigationItem will get the thin bar's color if I only use .shadow property.

So I used both and finally just assign the bar appearance to navigationItem's scrollEdgeAppearance where the content and the navigationBar aligns.

Nostrum answered 29/1 at 1:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.