Andrew
Andrew

Reputation: 16051

Advanced gradients?

Is it possibly to have a gradient where there's a colour in each corner of a UIView?

Upvotes: 0

Views: 1893

Answers (4)

Lars C.Hassing
Lars C.Hassing

Reputation: 35

Here is a working solution.

One vertical gradient bitmap mask used both as is and mirrored when drawing two color gradients.

FourTimesRGBAcomponents is the R,G,B,A components of the four corner colors UL, UR, LL and LR, i.e. ULR, ULG, ULB, ULA, URR etc.

void DrawFourGradientRect(CGContextRef Cgc,
                          const CGRect * Rect,
                          const CGFloat FourTimesRGBAcomponents[16])
{
   int            w;
   int            h;
   void          *MaskData;
   CGColorSpaceRef GrayCS;
   CGColorSpaceRef CS;
   CGContextRef   BC;           //Bitmap context
   CGFloat        GrayComp[4] = {1.0, 1.0, 0.0, 1.0}; //2 * Gray+Alpha
   CGGradientRef  Gradient;
   CGImageRef     Mask;

   w = Rect->size.width;
   h = Rect->size.height;

   //Start filling with white
   CGContextSetRGBFillColor(Cgc, 1, 1, 1, 1);
   CGContextFillRect(Cgc, *Rect);

   //Then create vertical gradient mask bitmap, white on top, black at bottom
   MaskData = malloc(w * h);
   if (!MaskData)
      return;
   GrayCS = CGColorSpaceCreateDeviceGray();
   if (!GrayCS)
   {
      free(MaskData);
      return;
   }
   BC = CGBitmapContextCreate(MaskData, w, h, 8, w, GrayCS, kCGImageAlphaNone);
   if (!BC)
   {
      CGColorSpaceRelease(GrayCS);
      free(MaskData);
      return;
   }
   Gradient = CGGradientCreateWithColorComponents(GrayCS, GrayComp, NULL, 2);
   if (!Gradient)
   {
      CGContextRelease(BC);
      CGColorSpaceRelease(GrayCS);
      free(MaskData);
      return;
   }
   CGColorSpaceRelease(GrayCS);
   CGContextDrawLinearGradient(BC, Gradient, CGPointZero, CGPointMake(0, h), 0);
   CGGradientRelease(Gradient);
   Mask = CGBitmapContextCreateImage(BC);
   CGContextRelease(BC);
   free(MaskData);
   if (!Mask)
      return;

   //Now draw first horizontal color gradient from UL to UR
   CS = CGColorSpaceCreateDeviceRGB();
   if (!CS)
   {
      CGImageRelease(Mask);
      return;
   }

   CGContextSaveGState(Cgc);
   CGContextTranslateCTM(Cgc, Rect->origin.x, Rect->origin.y);
   CGContextClipToMask(Cgc, CGRectMake(0, 0, w, h), Mask);

   Gradient = CGGradientCreateWithColorComponents(CS,
                                        FourTimesRGBAcomponents, NULL, 2);
   if (Gradient)
   {
      CGContextDrawLinearGradient(Cgc, Gradient, CGPointZero, CGPointMake(w, 0), 0);
      CGGradientRelease(Gradient);
   }

   CGContextRestoreGState(Cgc);

   //Finally draw second horizontal color gradient from LL to LR
   CGContextSaveGState(Cgc);
   CGContextTranslateCTM(Cgc, Rect->origin.x, Rect->origin.y + Rect->size.height);
   CGContextScaleCTM(Cgc, 1, -1);   //Use vertical gradient mask upside-down
   CGContextClipToMask(Cgc, CGRectMake(0, 0, w, h), Mask);

   Gradient = CGGradientCreateWithColorComponents(CS,
                                    FourTimesRGBAcomponents + 8, NULL, 2);
   if (Gradient)
   {
      CGContextDrawLinearGradient(Cgc, Gradient, CGPointZero, CGPointMake(w, 0), 0);
      CGGradientRelease(Gradient);
   }

   CGContextRestoreGState(Cgc);

   CGImageRelease(Mask);
   CGColorSpaceRelease(CS);
}                               /* DrawFourGradientRect                      */

Upvotes: 1

skue
skue

Reputation: 2077

There's no built-in way to do this, and no simple way to do this via Quartz either. However, you can do it via Quartz by creating 4 different 2-D gradients going in different directions, and masking each one with a black-white gradient running perpendicular.

Assuming your colors are defined as: tlColor, trColor, blColor, brColor (for top-left, top-right, etc.).

  1. Vertical gradient from tlColor (top) to blColor (bottom).
  2. Horizontal gradient from white (left) to black (right), convert to image, use as image mask with image from #1.
  3. Vertical gradient from trColor (top) to brColor (bottom).
  4. Horizontal gradient from black (left) to white (right), convert to image, use as image mask with image from #3.
  5. Horizontal gradient from blColor (left) to trColor (right).
  6. Vertical gradient from white (top) to black (bottom), convert to image, use as image mask with image from #5.
  7. Horizontal gradient from tlColor (left) to brColor (right).
  8. Vertical gradient from black (top) to white (bottom), convert to image, use as image mask with image from #7.
  9. Then just draw each of the 4 results to the view.

You might want to try this in Acorn or Photoshop first... so it's clear what's involved before you translate it into code. Then the Quartz reference @lxt pointed you to has sections on gradients (CGGradientRef is sufficient, don't try to use shadings), and a separate section on image masks.

Upvotes: 2

vikingosegundo
vikingosegundo

Reputation: 52237

I have 2 gradients placed diagonal. With overlay-blending

- (void)drawRect:(CGRect)rect 
{
    CGContextRef currentContext = UIGraphicsGetCurrentContext();

    size_t num_locations = 2;
    CGFloat locations[2] = { 0.0, 1.0 };
    CGFloat locations2[2] = { 1.0, 0.0 };
    CGFloat components[8] = { 
        1.0, 1.0, 0.0, 1.0 ,//Yellow
        0.0, 0.0, 1.0, 1.0};//Blue

    CGColorSpaceRef rgbColorspace = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);

    CGPoint point1 = CGPointMake(0, 0);
    CGPoint point2 = CGPointMake(self.bounds.size.width, self.bounds.size.height);
    CGContextDrawLinearGradient(currentContext, gradient, point1, point2, 0);

    CGFloat components1[8]  = { 
        0.0, 1.0, 0.0, 1.0,//Red 
        1.0, 0.0, 0.0, 1.0}; //Green

    gradient = CGGradientCreateWithColorComponents(rgbColorspace, components1, locations2, num_locations);

    CGContextSetBlendMode(currentContext, kCGBlendModeOverlay);

    point1 = CGPointMake(self.bounds.size.width, 0);
    point2 = CGPointMake(0, self.bounds.size.height);

    CGContextDrawLinearGradient(currentContext, gradient, point1, point2, 0);


    CGGradientRelease(gradient);
    CGColorSpaceRelease(rgbColorspace); 
}

To change the color you should have 4 UIColors as members on the view and change this fragment accordingly.

Upvotes: 1

lxt
lxt

Reputation: 31304

To be a little more helpful than Carl, the answer is 'yes', but how you implement it would depend on the tech you wanted to use. The Quartz gradient system is described here:

http://developer.apple.com/library/mac/#documentation/graphicsimaging/Conceptual/drawingwithquartz2d/dq_shadings/dq_shadings.html

...but OpenGL is another possibility. It would depend on your application.

Upvotes: 0

Related Questions