0

I would like to fill my UINavigationBar background with CGGradientRef instead of a picture file (e.g. myBackground.png). This practice will avoid having to create a PNG file for each screen size and also save storage space.

I've seen that it's possible to create an UIImage drawing a gradient from scratch and using:

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

Also, I've seen that I can assign an UIImage to a UINavigationBar using:

myNavigationController.navigationBar.barTintColor = [UIColor colorWithPatternImage:image]];

However, I have not been able to put these two together. Some help will be appreciated. Thank you.

AveLeon
  • 203
  • 1
  • 2
  • 11
  • If a CAGradientLayer is acceptable, there is a great library that can do this without much work, and you only need to supply your gradient colors. Check out https://github.com/chroman/CRGradientNavigationBar – Zee Apr 19 '15 at 16:29

1 Answers1

1

Here is a overloaded UINavigationBar class using a two point gradient, although it could easily be improved to cover multi-point gradients.

To enable this bar style, select the navigation bar object in the navigation scene of the storyboard, and set its custom class to GradientNavigationBar.

In this case the awakeFromNib call is used to change the background, (assuming that the Navigation bar class has been changed in the storyboard), in the case that the Navigation bar is instantiated programatically, the customization call should be made in the appropriate position in the code.

The solution works by converting the colors passed to it to an array of CGFloat, then generating a CGGradientRef object, using those colors, creating an image and then using the setBackgroundImage:forBarMetrics call to set the background as required.

@interface GradientNavigationBar

@end

@implementation GradientNavigationBar

-(void) awakeFromNib {
    [self setGradientBackground:[UIColor redColor] 
                       endColor:[UIColor yellowColor]];
}

-(void) setGradientBackground:(UIColor *) startColor endColor:(UIColor *) endColor {

    // Convert the colors into a format where they can be used with
    // core graphics

    CGFloat rs, gs, bs, as, re, ge, be, ae;
    [startColor getRed:&rs green:&gs blue:&bs alpha:&as];
    [endColor   getRed:&re green:&ge blue:&be alpha:&ae];
    CGFloat colors [] = {
        rs, gs, bs, as,
        re, ge, be, ae
    };

    // Generate an Image context with the appropriate options, it may
    // be enhanced to take into account that Navbar heights differ
    // eg between landscape and portrait in the iPhone
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
    CGContextRef gc = UIGraphicsGetCurrentContext();
    CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();

    // The gradient element indicates the colors to be used and 
    // the color space
    CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, colors, NULL, 2);
    CGColorSpaceRelease(baseSpace), baseSpace = NULL;

    // Draw the gradient
    CGContextDrawLinearGradient(gc, gradient, CGPointMake(0, 0),CGPointMake(0, self.bounds.size.height),0);

    // Capture the image
    UIImage * backgroundImage = UIGraphicsGetImageFromCurrentImageContext();

    // The critical call for setting the background image
    // Note that separate calls can be made e.g. for the compact
    // bar metric.
    [self setBackgroundImage:backgroundImage  forBarMetrics:UIBarMetricsDefault];

    CGGradientRelease(gradient), gradient = NULL;
    UIGraphicsEndImageContext();


}
@end
lance-ios
  • 41
  • 5
  • Thank you @lance-ios. How do I assign the custom `GradientNavigationBar` to my `UINavigationController`? I couldn't find a `setNavigationBar` function or similar. – AveLeon Apr 21 '15 at 14:58
  • @AveLeon I have edited the solution, assuming that you are using storyboards. Is this the case? – lance-ios Apr 22 '15 at 12:18
  • Hi @lance-ios. No, unfortunately, I am using NIBs. Can you please update the answer to include this case as well? – AveLeon Apr 22 '15 at 21:12
  • @AveLeon Look at this [Stackoverload answer](http://stackoverflow.com/questions/1869331/set-a-custom-subclass-of-uinavigationbar-in-uinavigationcontroller-programmatica) for how to set custom Navigation Bar classes: – lance-ios Apr 23 '15 at 10:08
  • I added: `GradientNavigationBar *gradientNB = [GradientNavigationBar new];` and `[myNC setValue:gradientNB forKeyPath:@"navigationBar"];` but the `UINavigationBar` background is still blank. Any suggestions? – AveLeon Apr 23 '15 at 22:25
  • Please try adding `[self setGradientBackground:[UIColor redColor] endColor:[UIColor yellowColor]]` after `GradientNavigationBar *gradientNB = [GradientNavigationBar new];` btw. The solution based on `UINavigationController *navigationController= [[UINavigationController alloc]initWithNavigationBarClass:[CustomNavBar class] toolbarClass:nil]; [navigationController setViewControllers:[NSArray arrayWithObject:yourRootViewController]];` looks like the most viable one, I would try that, and set break points to make sure that all the correct points are being reached. – lance-ios Apr 24 '15 at 09:27
  • I had no luck. Here is what I implemented: `GradientNavigationBar *gradientNB = [GradientNavigationBar new]; [gradientNB setGradientBackground:[UIColor blueColor] endColor:[UIColor redColor]]; UINavigationController *myNC = [[UINavigationController alloc] initWithNavigationBarClass:[GradientNavigationBar class] toolbarClass:nil]; [myNC setValue:gradientNB forKeyPath:@"navigationBar"]; [myNC setViewControllers:[NSArray arrayWithObject:myVC]];` Have you tried implementing your recommendation? – AveLeon Apr 24 '15 at 19:58
  • Hi @lance-ios, I am sorry to be persistent, but I was wondering if you tried the code that you provided. I am not having luck, but I think I'm close to getting it right. – AveLeon Apr 28 '15 at 02:03
  • @AveLeon The problem is probably that `initWithNavigationBarClass` creating its own NavBar instance. I suggest the following: first do `UINavigationController *myNC = [[UINavigationController alloc] initWithNavigationBarClass:[GradientNavigationBar class] toolbarClass:nil]` and then `[myNC setGradientBackground:[UIColor blueColor] endColor:[UIColor redColor]];` – lance-ios Apr 28 '15 at 07:54
  • Hi @lance-ios. `[myNC setGradientBackground:[UIColor blueColor] endColor:[UIColor redColor]];` gives me an error. Are you sure I can `setGradientBackground:` directly to `myNC`? – AveLeon Apr 28 '15 at 10:36
  • sorry [myNC.navigationBar setGradientBackground:[UIColor blueColor] endColor:[UIColor redColor]] – lance-ios Apr 28 '15 at 13:41