TIMEX
TIMEX

Reputation: 271704

How can I change the color of the UITabBar top border by creating a UIImage programmatically?

From my understanding, the only way to change the color of the top border is to set the background image (320x49, with pixel line at top). It seems to me that this is the only way (please correct me if I'm wrong).

Is there a way to do this without using an image file? For example, someone helped me change the NavigationBar bottom border by creating a UIImage from code:

UINavigationBar.appearance().shadowImage = UIImage.colorForNavBar(UIColor.redColor())

extension UIImage {   
    class func colorForNavBar(color: UIColor) -> UIImage {
        let rect = CGRectMake(0.0, 0.0, 1.0, 1.0)
        UIGraphicsBeginImageContext(rect.size)
        let context = UIGraphicsGetCurrentContext()

        CGContextSetFillColorWithColor(context, color.CGColor)
        CGContextFillRect(context, rect)

        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return image
    }
}

This solution actually works well; it changes the color of my bottom border.

I tried to apply this to the TabBar, but nothing changes at all.

UITabBar.appearance().shadowImage = UIImage.colorForNavBar(.redColor())

Upvotes: 16

Views: 16602

Answers (6)

Christopher Whidden
Christopher Whidden

Reputation: 2091

You've pretty much answered your own question. You can do the same thing with your UITabBar as you did with your UINavigationBar. If you want to change the shadow image (i.e. the "top border"), then you have to change the background image. Straight from Apple:

The custom shadow image for the tab bar. This attribute is ignored if the tab bar does not also have a custom background image. To set this attribute programmatically, use the shadowImage property.

In your own question you seem to be aware of this:

the only way to change the color of the top border is to set the background image (320x49, with pixel line at top)

Except that it's not the background image that has a line at the top. You just have to set the background image to anything, then you can set the shadow image to your preference.

If you open up the simple "tabbed application" template within Xcode, you'll find that adding these two lines of code (and your UIImage extension code) indeed work:

// White background with red border on top
UITabBar.appearance().backgroundImage = UIImage.colorForNavBar(.whiteColor())
UITabBar.appearance().shadowImage = UIImage.colorForNavBar(.redColor())

Tab Bar

Upvotes: 32

Kevin R
Kevin R

Reputation: 8621

You don't need an extension to create an image of a certain size, UIImage has a perfectly good constructor for that.

To prevent losing the translucent blur, you can set the bar tint color instead of a background image. You can also use the screen scale to make sure the border is one pixel, like the original border was:

    let hairlineHeight = CGFloat(1) / UIScreen.main.scale
    tabBar.barTintColor = .white
    tabBar.shadowImage = UIImage(color: .black, size: CGSize(width: 1, height: hairlineHeight))

Upvotes: 0

Fox5150
Fox5150

Reputation: 2199

Here is the Swift 3 solution:

extension UIImage {
    class func colorForNavBar(color: UIColor) -> UIImage {
        let rect = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0)
        //    Or if you need a thinner border :
        //    let rect = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 0.5)
        UIGraphicsBeginImageContext(rect.size)
        let context = UIGraphicsGetCurrentContext()

        context!.setFillColor(color.cgColor)
        context!.fill(rect)

        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return image!
    }
}

used with the code above in the viewDidLoad of the UITabBarController

UITabBar.appearance().backgroundImage = UIImage.colorForNavBar(color: .white)
UITabBar.appearance().shadowImage = UIImage.colorForNavBar(color: .red)

Upvotes: 14

Harry Bloom
Harry Bloom

Reputation: 2449

What about simply subclassing UITabBar and adding a new sublayer to the view in layoutSubviews.

Swift example:

override func layoutSubviews() {
    super.layoutSubviews()

    let topBorder = CALayer()

    let borderHeight: CGFloat = 2

    topBorder.borderWidth = borderHeight
    topBorder.borderColor = UIColor.redColor().CGColor
    topBorder.frame = CGRect(x: 0, y: -1, width: self.frame.width, height: borderHeight)

    self.layer.addSublayer(topBorder)
}

Upvotes: 3

Léo Natan
Léo Natan

Reputation: 57040

Normally, the other answers got it right - you have to set both a background image and a shadow image. However, doing so will cause the bar to drop its translucency (blur); even if you set a transparent image, the bar will be transparent, not translucent.

We also had a similar need, but we wanted to preserve the translucency of the bar. Instead of setting a shadow image, we subclassed the bar, and put a hairline subview with a color we want. When the bar lays out its subviews, we set the frame of the hairline to be the width of the bar, and a pixel exactly.

See my GitHub demo project.

Here is a screenshot of the result:

Colored hairline tab bar

After you include my subview in your project, just use the following line to set the color:

if let tabBar = tabBarController?.tabBar as? ColoredHairlineTabBar {
    tabBar.hairlineColor = ... //Your color
}

Upvotes: 2

eternity
eternity

Reputation: 483

You need to provide an different image for UINavigationBar.appearance().backgroundImage.

For example:

UINavigationBar.appearance().backgroundImage = UIImage.colorForNavBar(.blackColor())
UINavigationBar.appearance().shadowImage = UIImage.colorForNavBar(.redColor())

enter image description here

Upvotes: 3

Related Questions