views:

703

answers:

3

I have a gradient that has rounded corners that I an using for a custom UITableViewCell background. I am trying to apply a stroke to the path but cannot quite do it, and can't see where I am going wrong.

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

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

  CGContextMoveToPoint(c, minx, miny);
  CGContextAddArcToPoint(c, minx, maxy, midx, maxy, kDefaultMargin);
  CGContextAddArcToPoint(c, maxx, maxy, maxx, miny, kDefaultMargin);
  CGContextAddLineToPoint(c, maxx, miny);
  CGContextAddLineToPoint(c, minx, miny);

  // Fill and stroke the path
  CGContextClip(c);
  CGContextStrokePath(c);  

  CGFloat locations[2] = { 0.0, 1.0 };
  CGFloat mycomponents[8] = TABLE_CELL_BACKGROUND;
  CGColorSpaceRef myColorspace = CGColorSpaceCreateDeviceRGB();
  CGGradientRef myGradient = CGGradientCreateWithColorComponents(myColorspace, mycomponents, locations, 2);
  CGContextDrawLinearGradient(c, myGradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), 0);
  CGGradientRelease(myGradient);
  CGColorSpaceRelease(myColorspace);

This code rounds the rectangle right and applies the gradient, but does not stroke the row. Where is my mistake?

alt text



EDIT: As per subw's recommendations, I changed the code to be:

    CGContextRef c = UIGraphicsGetCurrentContext();
    CGColorSpaceRef myColorspace = CGColorSpaceCreateDeviceRGB();
    CGGradientRef myGradient = nil;
    CGFloat components[8] = TABLE_CELL_BACKGROUND;
    CGContextSetFillColorWithColor(c, [[UIColor redColor] CGColor]);
    CGContextSetStrokeColorWithColor(c, [[UAColor redColor] CGColor]);
    CGContextSetLineWidth(c, 2);

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

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

    CGContextMoveToPoint(c, minx, miny);
    CGContextAddArcToPoint(c, minx, maxy, midx, maxy, kDefaultMargin);
    CGContextAddArcToPoint(c, maxx, maxy, maxx, miny, kDefaultMargin);
    CGContextAddLineToPoint(c, maxx, miny);
    CGContextAddLineToPoint(c, minx, miny);

    // Fill and stroke the path
    CGContextSaveGState(c);
    CGContextClip(c);

    CGFloat locations[2] = { 0.0, 1.0 };
    CGFloat mycomponents[8] = TABLE_CELL_BACKGROUND;
    CGColorSpaceRef myColorspace = CGColorSpaceCreateDeviceRGB();
    myGradient = CGGradientCreateWithColorComponents(myColorspace, mycomponents, locations, 2);
    CGContextDrawLinearGradient(c, myGradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), 0);

    CGContextRestoreGState(c);
    CGContextStrokePath(c);

But the rect comes out like this…

alt text

...with no border. If I change the CGContextStrokePath(c) to be CGContextStrokeRect(c, rect), the stroke shows up (I made it red for clarification). But somehow, when stroking the path, I am not getting any stroke :(

alt text

A: 

Not sure, but maybe you're painting the gradient over the stroke?

Ole Begemann
I dont think so. I am drawing the stroke after the gradient in the updated code.
coneybeare
A: 

You're either drawing the gradient over the stroke, and/or clipping the region you stroke.

I think the following might work better:

CGContextSaveGState(c);
CGContextClip(c);

//gradient drawing stuff

CGContextRestoreGState(c);
CGContextStrokePath(c);

Of course, you also have to set the stroke color and width correctly.

mrueg
I thought this would do it but it didn't: http://grab.by/2714
coneybeare
One more question: do you have a call to CGContextBeginPath(c) somewhere? If not, that might be a problem.
mrueg
I do not. I have updated the code to show what I have before this section of the code that draws the cell.
coneybeare
Is the path drawn if you remove all the clipping, gradient drawing and saving and restoring the graphics state stuff? If so, Peter Hoseys answer is probably right.
mrueg
+2  A: 

Responding to your revised code, I quote the documentation:

Unlike the current path, the current clipping path is part of the graphics state. Therefore, to re-enlarge the paintable area by restoring the clipping path to a prior state, you must save the graphics state before you clip and restore the graphics state after you’ve completed any clipped drawing.

After determining the new clipping path, the function resets the context’s current path to an empty path.

CGContextClip

So, the current drawing path is not part of the graphics state. So here's what you're doing:

  1. Build drawing path
  2. Save gstate
  3. Add current drawing path to clipping path; clear current drawing path
  4. Draw gradient
  5. Restore gstate (previous clipping path is restored; drawing path remains empty)
  6. Stroke nothing

The solution is to draw the path into a CGPath object, then add it as the current path both before clipping (after saving the gstate) and before stroking.

You should also decide whether you want this to be an outer stroke, an inner stroke, or a centered stroke. For a centered stroke, stroke while not clipped. For an inner stroke, stroke while clipped. For an outer stroke, reverse the path, then clip, then stroke. You'll want to double your line width for both of the last two forms, since you'll be clipping out half of the stroke.

If I change the CGContextStrokePath(c) to be CGContextStrokeRect(c, rect), the stroke shows up.

Because that function adds a path of that rectangle to the current drawing path before stroking.

Peter Hosey
Why is the current path cleared when setting the clipping path? The docs for CGContextClip don't mention this.
mrueg
subw: Yes they do. “After determining the new clipping path, the function resets the context’s current path to an empty path.” It does this because that's how PostScript (PDF's immediate ancestor) does it.
Peter Hosey
Makes sense. I'll try this out tonight
coneybeare
works great. maybe my logic is not the best on the corners, but that is a new SO question to ask :)
coneybeare
Dear Core Graphics GURU, If you feel like helping on another question, it is here: http://stackoverflow.com/questions/2181279/smoothing-a-rounded-stroke-in-core-graphics
coneybeare