I'd like to change programmaticaly the tintColor
of a UINavigationBar and keep the gradient as in Interface Builder.
When I change the tintColor
in my code, the gradient disappears but when I change the tintColor
in Interface Builder, the gradient is kept.
Any ideas?
So this is an old question, but I stumbled upon it when I was searching for something that sounds like this question, but is different. In hopes that it will help someone else (or that someone else will show me a better way)...
How do you implement a custom gradient on a UINavigationBar?
First, let's get our custom gradient the way we normally would (As a CALayer):
- (CALayer *)gradientBGLayerForBounds:(CGRect)bounds
{
CAGradientLayer * gradientBG = [CAGradientLayer layer];
gradientBG.frame = bounds;
gradientBG.colors = @[ (id)[[UIColor redColor] CGColor], (id)[[UIColor purpleColor] CGColor] ];
return gradientBG;
}
There we go, a gradient that looks like a questionable make-up choice of a punk rocker, which is good, as this code is for the hypothetical app I Have A Punk-Rocker With Questionable Tastes In My Pocket. That name me be a little too long..
Now I want this layer on my UINavigationBar, which should be easy ya? Just add it as a sublayer, right? The problem here is it will cover up the wonderful buttons and stuff that the UINavigationController gives you. That's bad.
Instead let's look at the wonderful convenience method we have in iOS5+ for altering the appearance of all the UINavigationBar(s) in our app:
[[UINavigationBar appearance] setBackgroundImage:SOME_IMAGE
forBarMetrics:UIBarMetricsDefault];
The only problem here is we don't have an UIImage, we have a CALayer. What's a punk-rocker enthusiast to do?
CALayer * bgGradientLayer = [self gradientBGLayerForBounds:ONE_OF_YOUR_NAV_CONTROLLERS.navigationBar.bounds];
UIGraphicsBeginImageContext(bgGradientLayer.bounds.size);
[bgGradientLayer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * bgAsImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
So now we've converted our CALayer to an UIImage (hopefully), so all that remains is to set it:
if (bgAsImage != nil)
{
[[UINavigationBar appearance] setBackgroundImage:bgAsImage
forBarMetrics:UIBarMetricsDefault];
}
else
{
NSLog(@"Failded to create gradient bg image, user will see standard tint color gradient.");
}
What I like about this solution is
But I'm still not convinced it's the best way. So, enjoy if it helps you, let me know how to improve it if you can.
~ Thanks
Set the barStyle to UIBarStyleBlackTranslucent Set the tintColor like this:
navigationBar.tintColor = [UIColor colorWithRed:.7 green:.5 blue:.2 alpha:1];
This will set the appropriate gradient. Use whatever combination you need.
I just wrote it as UIImage extension for Swift 2.0. Maybe it's of some use.
extension UIImage {
class func convertGradientToImage(colors: [UIColor], frame: CGRect) -> UIImage {
// start with a CAGradientLayer
let gradientLayer = CAGradientLayer()
gradientLayer.frame = frame
// add colors as CGCologRef to a new array and calculate the distances
var colorsRef : [CGColorRef] = []
var locations : [NSNumber] = []
for i in 0 ... colors.count-1 {
colorsRef.append(colors[i].CGColor as CGColorRef)
locations.append(Float(i)/Float(colors.count))
}
gradientLayer.colors = colorsRef
gradientLayer.locations = locations
// now build a UIImage from the gradient
UIGraphicsBeginImageContext(gradientLayer.bounds.size)
gradientLayer.renderInContext(UIGraphicsGetCurrentContext()!)
let gradientImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// return the gradient image
return gradientImage
}
}
Call it like this:
let colors = [
UIColor.blueColor(),
UIColor.greenColor()
// and many more if you wish
]
let gradientImage = UIImage.convertGradientToImage(colors, frame: navigationBar.bounds)
navigationBar.setBackgroundImage(gradientImage, forBarMetrics: .Default)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With