TheoF
TheoF

Reputation: 795

Change navigation bar bottom border color Swift

It works with

import UIKit

class ViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    self.navigationController?.navigationBar.setBackgroundImage(UIImage(), forBarMetrics: .Default)
    self.navigationController?.navigationBar.shadowImage = UIColor.redColor().as1ptImage()


}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}


}

extension UIColor {
    func as1ptImage() -> UIImage {
        UIGraphicsBeginImageContext(CGSizeMake(1, 1))
        let ctx = UIGraphicsGetCurrentContext()
        self.setFill()
        CGContextFillRect(ctx, CGRect(x: 0, y: 0, width: 1, height: 1))
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
}

But when I add a UITableView it doesn't appear on it and when I add a UISearchView it appears but removes the navigation bar.

Anyone knows how to solve this?

Upvotes: 28

Views: 28255

Answers (8)

Muhammad Aamir Ali
Muhammad Aamir Ali

Reputation: 21097

For iOS 13 and later

    guard let navigationBar = navigationController?.navigationBar else { return }
    navigationBar.isTranslucent = true
    if #available(iOS 13.0, *) {
        let appearance = UINavigationBarAppearance()
        appearance.configureWithTransparentBackground()
        appearance.backgroundImage = UIImage()
        appearance.backgroundColor = .clear
        navigationBar.standardAppearance = appearance
    } else {
        navigationBar.setBackgroundImage(UIImage(), for: .default)
        navigationBar.shadowImage = UIImage()
    }

Upvotes: 2

MartijndeM
MartijndeM

Reputation: 176

There's a much better option available these days:

UINavigationBar.appearance().shadowImage = UIImage()

Upvotes: 1

Bios90
Bios90

Reputation: 811

Solution for Swift 4.0 - 5.2

Here is small extension for changing both Height and Color of bottom navbar line

extension UINavigationController
{
    func addCustomBottomLine(color:UIColor,height:Double)
    {
        //Hiding Default Line and Shadow
        navigationBar.setValue(true, forKey: "hidesShadow")
    
        //Creating New line
        let lineView = UIView(frame: CGRect(x: 0, y: 0, width:0, height: height))
        lineView.backgroundColor = color
        navigationBar.addSubview(lineView)
    
        lineView.translatesAutoresizingMaskIntoConstraints = false
        lineView.widthAnchor.constraint(equalTo: navigationBar.widthAnchor).isActive = true
        lineView.heightAnchor.constraint(equalToConstant: CGFloat(height)).isActive = true
        lineView.centerXAnchor.constraint(equalTo: navigationBar.centerXAnchor).isActive = true
        lineView.topAnchor.constraint(equalTo: navigationBar.bottomAnchor).isActive = true
    }
}

And after adding this extension, you can call this method on any UINavigationController (e.g. from ViewController viewDidLoad())

self.navigationController?.addCustomBottomLine(color: UIColor.black, height: 20)

Upvotes: 5

nontomatic
nontomatic

Reputation: 2043

From iOS 13 on, you can use the UINavigationBarAppearance() class with the shadowColor property:

if #available(iOS 13.0, *) {
  let style = UINavigationBarAppearance()
  style.shadowColor = UIColor.clear // Effectively removes the border
  navigationController?.navigationBar.standardAppearance = style

  // Optional info for follow-ups:
  // The above will override other navigation bar properties so you may have to assign them here, for example:
  //style.buttonAppearance.normal.titleTextAttributes = [.font: UIFont(name: "YourFontName", size: 17)!]
  //style.backgroundColor = UIColor.orange
  //style.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white,
                               NSAttributedString.Key.font: UIFont(name: "AnotherFontName", size: 20.0)!]
} else {
  // Fallback on earlier versions
}

Upvotes: 4

Claus
Claus

Reputation: 5722

Putting @alessandro-orru's answer in one extension

extension UINavigationController {

    func setNavigationBarBorderColor(_ color:UIColor) {
        self.navigationBar.shadowImage = color.as1ptImage()
    }
}

extension UIColor {

    /// Converts this `UIColor` instance to a 1x1 `UIImage` instance and returns it.
    ///
    /// - Returns: `self` as a 1x1 `UIImage`.
    func as1ptImage() -> UIImage {
        UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
        setFill()
        UIGraphicsGetCurrentContext()?.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
        let image = UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
        UIGraphicsEndImageContext()
        return image
    }
}

then in your view controller just add:

self.navigationController?.setNavigationBarBorderColor(UIColor.red)

Upvotes: 4

backslash-f
backslash-f

Reputation: 8193

Wonderful contributions from @TheoF, @Alessandro and @Pavel.

Here is what I did for...

Swift 4

extension UIColor {

    /// Converts this `UIColor` instance to a 1x1 `UIImage` instance and returns it.
    ///
    /// - Returns: `self` as a 1x1 `UIImage`.
    func as1ptImage() -> UIImage {
        UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
        setFill()
        UIGraphicsGetCurrentContext()?.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
        let image = UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
        UIGraphicsEndImageContext()
        return image
    }
}

Using it in viewDidLoad():

/* In this example, I have a ViewController embedded in a NavigationController in IB. */

// Remove the background color.
navigationController?.navigationBar.setBackgroundImage(UIColor.clear.as1ptImage(), for: .default)

// Set the shadow color.
navigationController?.navigationBar.shadowImage = UIColor.gray.as1ptImage()

Upvotes: 24

Pavel
Pavel

Reputation: 61

for Swift 3.0 just change this line:

CGContextFillRect(ctx, CGRect(x: 0, y: 0, width: 1, height: 1))

to this:

ctx?.fill(CGRect(x: 0, y: 0, width: 1, height: 1))

Upvotes: 1

Alessandro Orrù
Alessandro Orrù

Reputation: 3513

You have to adjust the shadowImage property of the navigation bar.

Try this one. I created a category on UIColor as an helper, but you can refactor the way you prefer.

extension UIColor {
    func as1ptImage() -> UIImage {
        UIGraphicsBeginImageContext(CGSizeMake(1, 1))
        let ctx = UIGraphicsGetCurrentContext()
        self.setFill()
        CGContextFillRect(ctx, CGRect(x: 0, y: 0, width: 1, height: 1))
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
}

Option 1: on a single navigation bar

And then in your view controller (change the UIColor to what you like):

// We can use a 1px image with the color we want for the shadow image
self.navigationController?.navigationBar.shadowImage = UIColor.redColor().as1ptImage()

// We need to replace the navigation bar's background image as well 
// in order to make the shadowImage appear. We use the same 1px color tecnique
self.navigationController?.navigationBar.setBackgroundImage(UIColor.yellowColor‌​().as1ptImage(), forBarMetrics: .Default)    

Option 2: using appearance proxy, on all navigation bars

Instead of setting the background image and shadow image on each navigation bar, it is possible to rely on UIAppearance proxy. You could try to add those lines to your AppDelegate, instead of adding the previous ones in the viewDidLoad.

// We can use a 1px image with the color we want for the shadow image
UINavigationBar.appearance().shadowImage = UIColor.redColor().as1ptImage()

// We need to replace the navigation bar's background image as well 
// in order to make the shadowImage appear. We use the same 1px color technique
UINavigationBar.appearance().setBackgroundImage(UIColor.yellowColor().as1ptImage(), forBarMetrics: .Default)

Upvotes: 46

Related Questions