views:

61

answers:

1

Hi all!

I'm trying to make a more sophisticated drawing of a UILabels (or a UIView, putting the UILabel on top, should that be required) background. Since I want this to resize automatically when the user changes iPhone orientation, I am trying to implement this in a subclasses drawRect function. I would love to be able to tune this all in code, omitting the need for pattern images, if it's possible.

I got some hints from some former posts on the subject:

Gradients on UIView and UILabels On iPhone and Make Background of UIView a Gradient Without Sub Classing

Unfortunately they both miss the target, since I wanted to combine gradient with custom drawing. I want the CGGradient to be clipped by the line frame I am drawing (visible at the rounded corners) but I can't accomplish this.

The alternative would be to use the CAGradientLayer, which seem to work quite good, but that would require some resising of the frame at rotation, and the gradient layer seem to draw on top of the text, no matter how I try.

My question therefor is twofold

  • How do I modify the code below to make the gradient clip to the drawn "frame"
  • How do I make CAGradientLayer draw behind the text of the UILabel (or do I have to put a UIView behind the UILabel with the CAGradientLayer as background)

Here is my drawrect function:

- (void)drawRect:(CGRect)rect {
// Drawing code
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(c, [[UIColor clearColor] CGColor]);
CGContextSetStrokeColorWithColor(c, [strokeColor CGColor]);
CGContextSetLineWidth(c, 1);

CGFloat minx = CGRectGetMinX(rect), midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ;
CGFloat miny = CGRectGetMinY(rect), midy = CGRectGetMidY(rect), maxy = CGRectGetMaxY(rect) ;
minx = minx + 1;
miny = miny + 1;

maxx = maxx - 1;
maxy = maxy - 1;

CGGradientRef glossGradient;
CGColorSpaceRef rgbColorspace;
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat components[8] = { 0.6, 0.6, 0.6, 1.0,  // Start color
0.3, 0.3, 0.3, 1.0 }; // End color

rgbColorspace = CGColorSpaceCreateDeviceRGB();
glossGradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);

CGRect currentBounds = [self bounds];
CGPoint topCenter = CGPointMake(CGRectGetMidX(currentBounds), 0.0f);
CGPoint midCenter = CGPointMake(CGRectGetMidX(currentBounds),            CGRectGetMidY(currentBounds));
CGPoint lowCenter = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMidY(currentBounds));

CGContextDrawLinearGradient(c, glossGradient, topCenter, midCenter, 0);

CGGradientRelease(glossGradient);
CGColorSpaceRelease(rgbColorspace); 

CGContextMoveToPoint(c, minx, midy);
CGContextAddArcToPoint(c, minx, miny, midx, miny, ROUND_SIZE_INFO);
CGContextAddArcToPoint(c, maxx, miny, maxx, midy, ROUND_SIZE_INFO);
CGContextAddArcToPoint(c, maxx, maxy, midx, maxy, ROUND_SIZE_INFO);
CGContextAddArcToPoint(c, minx, maxy, minx, midy, ROUND_SIZE_INFO);

// Close the path
CGContextClosePath(c);
// Fill & stroke the path
CGContextDrawPath(c, kCGPathFillStroke);                
//        return;  

[super drawRect: rect]; 
A: 

What you are looking for is CGContextClip()

Create your path and gradient as in your code, and then

CGContextClip(c);
CGContextDrawLinearGradient(c, glossGradient, topCenter, midCenter, 0);
CGGradientRelease(glossGradient);

(Remove your old call to CGContextDrawLinearGradient)

Also remember to save and restore your context before and after you do any clipping

If your clipping is inverted from what you want, try CGContextEOClip() (or draw your path in reverse order)

Olof Hedman
Thanks, I'll look into it asap!
jollyCocoa