2

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]; 
Community
  • 1
  • 1
jollyCocoa
  • 691
  • 7
  • 20
  • Basically, I'd like to know if there's a way to limit the Gradient to a certain boundary, a bit like the maskToBounds flag of CALayer. – jollyCocoa Oct 03 '10 at 08:10
  • Using CAGradientLayer seems adequate for solving simple rectangular, gradient backgrounds, but the question remains if possible to accomplish the same task with custom drawing... (what if, for instance I would like to draw a custom bordered background with a non square border using CGContextAddLineToPoint() and CGContextAddArcToPoint(). Would it be possible to fill the contents of the border with a gradient and how would this be accomplished)? – jollyCocoa Oct 04 '10 at 07:32

1 Answers1

3

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
  • 613
  • 6
  • 17
  • I get back to this subject. It's been a while and I hope you will read this. I managed to create a gradient with clipped bounds according to the path, but I still have a problem I've not managed to solve. In the code above I create a path of a rectangle with rounded corners. In the original drawing this resulted in a figure with a drawn boundary and another fill color. Now I'd like to add a custom gradient to this shape. The problem is, your fix either lets me draw the original shape with an unclipped gradient above, or only the gradient clipped to the path. So how do I accomplish both? – jollyCocoa Jan 23 '11 at 17:07
  • I solved it. The solution was to draw a new path after the first path had been struck. Thanks alot! – jollyCocoa Jan 23 '11 at 19:09