Search code examples
iosobjective-cquartz-graphics

Gradient Filling a PNG with Quartz


How can I fill the non-transparent areas of a PNG UIImage with a linear gradient? I'd like to reuse a PNG shape for MKAnnotationViews, but change the gradient per annotation's properties.


Solution

  • To use an image as a mask for a gradient (i.e. to have a gradient in the shape of the non-transparent pixels of your image), you can:

    • create a simple view with a gradient (you can either create a simple UIView and use the addGradientLayerToView shown below to give it a gradient or you can create the gradient PNG in advance and add it to your bundle).

    • apply your PNG as a mask to that gradient view:

      UIImage *mask = [UIImage imageNamed:@"mask.png"];
      CALayer *maskLayer = [CALayer layer];
      maskLayer.frame = CGRectMake(0, 0, mask.size.width, mask.size.height);
      maskLayer.contents = (id)[mask CGImage];
      gradientViewToMask.layer.mask = maskLayer;
      

    To apply a gradient to the transparent pixels, you can either:

    1. Create a new image with a gradient:

      - (UIImage *)imageWithGradient:(UIImage *)image
      {
          UIGraphicsBeginImageContextWithOptions(image.size, NO, 1.0);
      
          CGContextRef context = UIGraphicsGetCurrentContext();
      
          size_t locationCount = 2;
          CGFloat locations[2] = { 0.0, 1.0 };
          CGFloat components[8] = { 0.0, 0.8, 0.8, 1.0,   // Start color
                                    0.9, 0.9, 0.9, 1.0 }; // End color
      
          CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
      
          CGGradientRef gradient = CGGradientCreateWithColorComponents (colorspace, components, locations, locationCount):
      
          CGPoint startPoint = CGPointMake(0.0, 0.0);
          CGPoint endPoint   = CGPointMake(0.0, image.size.height);
          CGContextDrawLinearGradient (context, gradient, startPoint, endPoint, 0);
      
          CGContextTranslateCTM(context, 0, image.size.height);
          CGContextScaleCTM(context, 1.0, -1.0);
      
          CGContextDrawImage(context, CGRectMake(0.0, 0.0, image.size.width, image.size.height), [image CGImage]);
      
          UIImage *gradientImage = UIGraphicsGetImageFromCurrentImageContext();
          UIGraphicsEndImageContext();
      
          CGGradientRelease(gradient);
          CGColorSpaceRelease(colorspace);
      
          return gradientImage;
      }
      
    2. You can also add a CAGradientLayer to a view and then add the UIImageView as a subview of that view.

      - (void)addGradientLayerToView:(UIView *)view
      {
          CAGradientLayer *gradient = [CAGradientLayer layer];
          gradient.frame = view.bounds;
          gradient.colors = @[(id)[[UIColor colorWithRed:0.0 green:0.8 blue:0.8 alpha:1.0] CGColor],
                              (id)[[UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1.0] CGColor]];
          [view.layer insertSublayer:gradient atIndex:0];
      }
      

      Note, you have to #import <QuartzCore/QuartzCore.h> as well as add the QuartzCore framework to your project.