29

Is it possible to change the grey border-bottom color of the UINavigationBar in iOS7?

I already tried to remove to border, but this is not working:

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

Thanks!

MappleDev
  • 453
  • 1
  • 6
  • 11
  • This will help: http://stackoverflow.com/questions/19226965/how-to-hide-ios7-uinavigationbar-1px-bottom-line – Niraj Mar 29 '16 at 07:34

15 Answers15

63

You are removing the shadow but not the border, you need to do the following:

[[UINavigationBar appearance] setBackgroundImage:[[UIImage alloc] init] forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setShadowImage:[[UIImage alloc] init]];

To change the border use an image of 2 pixels width line:

[[UINavigationBar appearance] setShadowImage:[UIImage imageNamed:@"2pxWidthLineImage"]]; 
Tarek Hallak
  • 18,422
  • 7
  • 59
  • 68
61

Here is a category to change bottom color with height:

[self.navigationController.navigationBar setBottomBorderColor:[UIColor redColor] height:1];

enter image description here

Objective C:

UINavigationBar+Helper.h

#import <UIKit/UIKit.h>

@interface UINavigationBar (Helper)
- (void)setBottomBorderColor:(UIColor *)color height:(CGFloat)height;
@end

UINavigationBar+Helper.m

#import "UINavigationBar+Helper.h"

@implementation UINavigationBar (Helper)

- (void)setBottomBorderColor:(UIColor *)color height:(CGFloat)height {
    CGRect bottomBorderRect = CGRectMake(0, CGRectGetHeight(self.frame), CGRectGetWidth(self.frame), height);
    UIView *bottomBorder = [[UIView alloc] initWithFrame:bottomBorderRect];
    [bottomBorder setBackgroundColor:color];
    [self addSubview:bottomBorder];
}
@end

Swift:

extension UINavigationBar {

    func setBottomBorderColor(color: UIColor, height: CGFloat) {
        let bottomBorderRect = CGRect(x: 0, y: frame.height, width: frame.width, height: height)
        let bottomBorderView = UIView(frame: bottomBorderRect)
        bottomBorderView.backgroundColor = color
        addSubview(bottomBorderView)
    }
}
sash
  • 8,423
  • 5
  • 63
  • 74
9

Here is another way:

CALayer *border = [CALayer layer];
border.borderColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"border"]].CGColor;
border.borderWidth = 1;
CALayer *layer = self.navigationController.navigationBar.layer;
border.frame = CGRectMake(0, layer.bounds.size.height, layer.bounds.size.width, 1);
[layer addSublayer:border];
k06a
  • 17,755
  • 10
  • 70
  • 110
7

The only way I found to change color is:

override func viewDidLoad() {
    super.viewDidLoad()

    if let navigationController = self.navigationController {
        let navigationBar = navigationController.navigationBar
        let navigationSeparator = UIView(frame: CGRectMake(0, navigationBar.frame.size.height - 1, navigationBar.frame.size.width, 0.5))
        navigationSeparator.backgroundColor = UIColor.redColor() // Here your custom color
        navigationSeparator.opaque = true
        self.navigationController?.navigationBar.addSubview(navigationSeparator)
    }

}
Luca Davanzo
  • 21,000
  • 15
  • 120
  • 146
5

I wrote an extension based on the other answers for easier usage in Swift:

extension UINavigationBar {

    func setBottomBorderColor(color: UIColor) {

        let navigationSeparator = UIView(frame: CGRectMake(0, self.frame.size.height - 0.5, self.frame.size.width, 0.5))
        navigationSeparator.backgroundColor = color
        navigationSeparator.opaque = true
        navigationSeparator.tag = 123
        if let oldView = self.viewWithTag(123) {
            oldView.removeFromSuperview()
        }
        self.addSubview(navigationSeparator)

    }
}

You can use this extension with calling the method in a context like that:

self.navigationController?.navigationBar.setBottomBorderColor(UIColor.whiteColor())

I found that pretty useful as I had to deal with that colored-border-problem.

FTFT1234
  • 435
  • 8
  • 17
5

I solved this problem with the use of autolayouts. The solution works on different screen sizes and with orientation change.

extension UINavigationBar {

    @IBInspectable var bottomBorderColor: UIColor {
        get {
            return self.bottomBorderColor;
        }
        set {
            let bottomBorderRect = CGRect.zero;
            let bottomBorderView = UIView(frame: bottomBorderRect);
            bottomBorderView.backgroundColor = newValue;
            addSubview(bottomBorderView);

            bottomBorderView.translatesAutoresizingMaskIntoConstraints = false;

            self.addConstraint(NSLayoutConstraint(item: bottomBorderView, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: 0));
            self.addConstraint(NSLayoutConstraint(item: bottomBorderView, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 0));
            self.addConstraint(NSLayoutConstraint(item: bottomBorderView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0));
            self.addConstraint(NSLayoutConstraint(item: bottomBorderView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute,multiplier: 1, constant: 1));
        }

    }

}
4

Based on the answer from @sash I made an extension in Swift using Autolayout, explained right here.

In essence, the other solutions have the following pitfalls:

  • Cannot add dropshadow if using the UIImage solution
  • The subview added doesn't resize upon rotation of the view
extension UINavigationBar {

  func setBottomBorderColor(color: UIColor, height: CGFloat) -> UIView {

    let bottomBorderView = UIView(frame: CGRectZero)
    bottomBorderView.translatesAutoresizingMaskIntoConstraints = false
    bottomBorderView.backgroundColor = color

    self.addSubview(bottomBorderView)

    let views = ["border": bottomBorderView]
    self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[border]|", options: [], metrics: nil, views: views))
    self.addConstraint(NSLayoutConstraint(item: bottomBorderView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: height))
    self.addConstraint(NSLayoutConstraint(item: bottomBorderView, attribute: .Bottom, relatedBy: .Equal, toItem: self, attribute: .Bottom, multiplier: 1.0, constant: height))    

    return bottomBorderView
  }
}

This let you still add a drop shadow if you need to, and this handles rotation nicely !

Community
  • 1
  • 1
Pierre
  • 1,053
  • 7
  • 17
2

If you like simple and hacky solutions like I do, create a view that covers the default border:

UIView *navBarLineView = [[UIView alloc] initWithFrame:CGRectMake(0, CGRectGetHeight(self.navigationController.navigationBar.frame),
                                                                  CGRectGetWidth(self.navigationController.navigationBar.frame), 1)];
navBarLineView.backgroundColor = [UIColor redColor];
[self.navigationController.navigationBar addSubview:navBarLineView];
budiDino
  • 13,044
  • 8
  • 95
  • 91
2

budidino solutions works very well. Here it is for Swift:

let navBarLineView = UIView(frame: CGRectMake(0,
    CGRectGetHeight((navigationController?.navigationBar.frame)!),
    CGRectGetWidth((self.navigationController?.navigationBar.frame)!),
    1))

navBarLineView.backgroundColor = UIColor.whiteColor()

navigationController?.navigationBar.addSubview(navBarLineView)
linker
  • 123
  • 2
  • 3
  • Here's a subclass in swift that does this with auto layout https://gist.github.com/soffes/0f87e42f1124d875cf754a44c9150704 – Sam Soffes Jun 03 '16 at 22:03
1

Well, if you want to remove bottom border you set shadow image to empty image

[navigationBar setShadowImage:[UIImage new]];

so if you want to set it to another color just create image with that color, I use a helper function to create image from color below (original source http://jslim.net/blog/2014/05/05/ios-customize-uitabbar-appearance/)

+ (UIImage *)imageFromColor:(UIColor *)color forSize:(CGSize)size 
{
return [UIImage imageFromColor:color forSize:size withCornerRadius:0];
}

+ (UIImage *)imageFromColor:(UIColor *)color forSize:(CGSize)size     withCornerRadius:(CGFloat)radius
{
CGRect rect = CGRectMake(0, 0, size.width, size.height);
UIGraphicsBeginImageContext(rect.size);

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

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

// Begin a new image that will be the new image with the rounded corners
// (here with the size of an UIImageView)
UIGraphicsBeginImageContext(size);

// Add a clip before drawing anything, in the shape of an rounded rect
[[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius] addClip];
// Draw your image
[image drawInRect:rect];

// Get the image, here setting the UIImageView image
image = UIGraphicsGetImageFromCurrentImageContext();

// Lets forget about that we were drawing
UIGraphicsEndImageContext();

return image;
}

and in my navbar

[navigationBar setShadowImage:[UIImage imageFromColor:[UIColor redColor] forSize:CGSizeMake(CGRectGetWidth(self.tableView.frame), 1)]];

that's it, It's working for me, hope this help. Please consider changing the accepted answer because its not working and can be confusing

Ansel
  • 360
  • 1
  • 10
1

To build on @sash's Swift implementation you can make the border responsive to rotation/trait changes by using constraints:

extension UINavigationBar {
  func setBottomBorderColor(color: UIColor, height: CGFloat) {

    let bottomBorderView = UIView()
    bottomBorderView.backgroundColor = color
    bottomBorderView.translatesAutoresizingMaskIntoConstraints = false
    addSubview(bottomBorderView)

    // Add constraints to make the bar always stay at the bottom of the nav bar and change size with rotation/trait changes
    let horizontalConstraint = NSLayoutConstraint(item: bottomBorderView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: self, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0)
    let verticalConstraint = NSLayoutConstraint(item: bottomBorderView, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: self, attribute: NSLayoutAttribute.bottom, multiplier: 1, constant: 0)
    let widthConstraint = NSLayoutConstraint(item: bottomBorderView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: self, attribute: .width, multiplier: 1, constant: 0)
    let heightConstraint = NSLayoutConstraint(item: bottomBorderView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: height)

    self.addConstraints([horizontalConstraint, verticalConstraint, widthConstraint, heightConstraint])
  }
}
g0ld2k
  • 519
  • 4
  • 17
0

Here's the method for creating image with clear color:

+ (UIImage*)imageFromColor:(UIColor *)color withSize:(CGSize)sizeImage
{
    UIImage *resultImage = nil;

    UIGraphicsBeginImageContext(sizeImage);

    CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), color.CGColor);
    CGContextFillRect(UIGraphicsGetCurrentContext(), CGRectMake(0.0f, 0.0f, sizeImage.width, sizeImage.height));
    resultImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return resultImage;
}

Here's it's usage for removing annoying bottom line:

navigationBar.shadowImage = [UIImage imageFromColor:[UIColor clearColor] withSize:CGSizeMake(1.0f, 1.0f)];
Eugene Alexeev
  • 1,152
  • 12
  • 32
0

Picture 1

you can use Reveal to see the border color is the UIImageView's backgroundColor. so directly modifying the imageView's backgroundColor or hide it.

the code: i write in @interface QdtTabBarController : UITabBarController

Class backGroundClass = NSClassFromString(@"_UIBarBackground");
for (UIView *view in self.tabBar.subviews) {
    if ([view isKindOfClass:backGroundClass]) {
        for (UIView *view2 in view.subviews) {
            if ([view2 isKindOfClass:[UIImageView class]]) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    view2.backgroundColor = [UIColor redColor];
                });
            };
        };
        break;
    }
}

Picture 2

Q.Man
  • 1
  • 2
-1

I'm using RubyMotion with the RedPotion gem, which includes a StandardAppearance class. This is what I did!

Put this line at the top of your app_delegate.rb, just before the on_load method:

ApplicationStylesheet.new(nil).application_setup

Then, in your application_stylesheet.rb, put this as the last line in the application_setup method:

StandardAppearance.apply app.window

And then this is my StandardAppearance class:

class StandardAppearance
  def self.apply(window)
    Dispatch.once do

      UINavigationBar.appearance.tap do |o|
        o.setBackgroundImage(UIImage.alloc.init, forBarMetrics: UIBarMetricsDefault)
        o.shadowImage = UIImage.alloc.init
      end

    end
  end
end
Eli Duke
  • 454
  • 1
  • 3
  • 14
  • Why did this get down voted? Can someone please explain themselves?! I was trying to solve this problem using RubyMotion and this was my solution. I thought it certainly couldn't hurt to include it here. – Eli Duke Jan 29 '16 at 19:45
  • I am not sure how your answer would help the poster here. The poster isn't even using RubyMotion and/or RedPotion. Submitting an irrelevant answer can misguide readers who are looking for a solution to a specific question. Yes it would definitely hurt the quality of this submission. Even if your answer was right, how likely is the poster going to switch from Objective-C to RubyMotion just for the sake of changing border color? ? ?`application_stylesheet.rb` isn't even a thing in iOS/Objective-C/Swift. – denniss Jan 07 '17 at 22:45
  • @denniss I'm not proposing that someone switch to RubyMotion for one fix. That wouldn't even be possible. But when it comes to iOS development, there are some different approaches: you can use Xcode straight up, you can use Swift, you can use RubyMotion, etc. There is actually a solution up there where someone mentions how to accomplish it with Swift. Did you vote them down as well? I was just thinking if someone was search for "Change UINavigationBar border rubymotion" then maybe they might find my solution. Also, I have been helped by similar posts on other questions. – Eli Duke Jan 08 '17 at 23:08
  • And you can use JavaScript, C, C++, Scheme...and there's this framework that lets you to build iOS using Java.... would you say submissions using those languages/frameworks are acceptable here? No, I wouldn't vote down submission using Swift because it's one of the officially supported language for iOS development and it's fairly easy to convert Swift to Obj-C (vice versa). I mean look at the Apple docs.... you can choose to view the code examples using Swift or Obj-C. I don't see Ruby as one of the options. – denniss Jan 09 '17 at 05:54
  • @denniss Ok. Fair enough. It's been very helpful to me in the past when people posted their RubyMotion solutions to iOS problems, so I guess I'll just accept the down vote. It's interesting, though, because you are coming from the approach that my answer won't help the person who posted the original question, which is correct. I wasn't trying to help that person, clearly, considering I was providing a possible solution to the problem nearly 2.5 years later! Again, I was just trying to add some google juice for my fellow RubyMotion developers. – Eli Duke Jan 09 '17 at 21:10
  • @denniss Just noticed that one of my other examples of this (giving a possible RubyMotion answer to a question) just got up-voted over here: http://stackoverflow.com/questions/813068/uitableview-change-section-header-color/35468043#35468043. So, it's not all bad, is it? ;) – Eli Duke May 10 '17 at 00:18
-11

This will help you :)

[self.navigationController.navigationBar.layer setBorderWidth:2.0];// Just to make sure its working
[self.navigationController.navigationBar.layer setBorderColor:[[UIColor redColor] CGColor]];
Adnan Aftab
  • 14,377
  • 4
  • 45
  • 54
  • 14
    This is a border around whole UINavigation bar, which is not the border author is trying to change. It does not help, when trying to set the bottom border. – Legoless Jan 20 '14 at 08:25