1

First of all, I did search this before posting this question but if there is an answer out there, it is buried under the millions of questions about how to remove the default bottom border of a navigation bar.

I do NOT want to remove the bottom border ("shadow") of the navigation bar.


I am trying to "theme" my app by the usual method of using appearance proxies. I can globaly change most visual attributes of UINavigationBar with code like the following:

let navigationBarProxy = UINavigationBar.appearance()
navigationBarProxy.isTranslucent = false
navigationBarProxy.barTintColor = myBarBackgroundColor
navigationBarProxy.tintColor = myBarTextColor

Regarding the 'hairline' bottom border of the bar (or as it is known, the "shadow"), I can either set the default one by doing nothing or specifying nil:

navigationBarProxy.shadowImage = nil

...or I can specify a custom color by assigning a solid image of the color I'm after:

navigationBarProxy.shadowImage = UIImage.withColor(myBorderColor)

(uses helper extension:)

extension UIImage {
    public static func withColor(_ color: UIColor?, size: CGSize = CGSize(width: 1, height: 1)) -> UIImage? {
        let rect = CGRect(origin: CGPoint.zero, size: size)
        UIGraphicsBeginImageContext(rect.size)
        let context = UIGraphicsGetCurrentContext()

        let actualColor = color ?? .clear
        context?.setFillColor(actualColor.cgColor)
        context?.fill(rect)

        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return image
    }
}

However, the approach above gives me (on retina devices) a 1pt, 2px border, whereas the default, light gray one is actually 0.5pt, 1px (a.k.a. "hairline").

Is there any way to achieve a 0.5 pt (1px), custom-colored bottom border (shadow) for UINavigationBar?


I guess I could use a runtime generated, background image that is for the most part solid, but has a 1px border of my choice color "baked in" at the bottom. But this seems inelegant at best, and I'm not sure how it would work when that navigation bar height changes: is the image sliced, or simply stretched, or what?

Nicolas Miari
  • 16,006
  • 8
  • 81
  • 189
  • Just curious to know if you provide default size with height of `0.5`, what do you get? – nayem May 22 '18 at 06:50
  • @nayem I tried specifying the size of the image I pass. Setting a height of 0.5 didn't make a difference (I still get a 2px - 1pt height). – Nicolas Miari May 22 '18 at 06:58

2 Answers2

1

Based on the chosen answer found here (with small changes because it was old):

How to change the border color below the navigation bar?

// in viewDidLoad
UIView * navBorder = [[UIView alloc] initWithFrame:CGRectMake(0,
                                                              self.navigationController.navigationBar.frame.size.height, // <-- same height, not - 1
                                                              self.navigationController.navigationBar.frame.size.width,
                                                              1/[UIScreen mainScreen].scale)]; // <-- 5/5S/SE/6 will be 0.5, 6+/X will be 0.33
// custom color here
[navBorder setBackgroundColor:customColor];

[self.navigationController.navigationBar addSubview:navBorder];


Credit here for finding scale programmatically: Get device image scale (e.g. @1x, @2x and @3x)

*NOTE:
iPhone 6+/X are x3, so 1px height will be 0.33pt
iPhone 5/5S/SE/6 are x2, so 1px height will be 0.5pt
Tested in simulator, may need to verify on actual devices.

Visually same as default nav bar with a custom color.

  • Thank you. This is the somewhat inelegant type of solution I was hoping to avoid, but from the looks of the available public APIs, it seems impossible. Clearly, Apple is doing something special if passing `nil` as the image gets you a specific gray in a specific thickness. – Nicolas Miari May 22 '18 at 02:24
  • If no one chimes in with a better solution within a few more days, I'll give you the tick. – Nicolas Miari May 23 '18 at 01:24
-1

I believe you want to remove the shadow. This should help with that.

[[UINavigationBar appearance] setShadowImage:[UIImage new]];

If you want to have different coloured shadow then you can create a image with your desired colour and use it instead of

[UIImage new]

You can use something like this for generating an image yourself

+ (UIImage *)imageWithColor:(UIColor *)color {
    CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
    const CGFloat alpha = CGColorGetAlpha(color.CGColor);
    const BOOL opaque = alpha == 1;
    UIGraphicsBeginImageContextWithOptions(rect.size, opaque, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, rect);

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
}
Prasad Parab
  • 381
  • 1
  • 12
  • No, I do **not** want to remove the shadow. I want to change the color of the shadow, while retaining the original 1px width. Please read my question again. – Nicolas Miari May 23 '18 at 01:22