Drawing a curved highlight tint at the top of a rounded NSView, similar to that seen with the Quartz Composer UI
cocoa, core-graphics, nsview, quartz-graphics
Solution
One way to achieve the effect you want is to use a bezier paths winding rule. Following your example this code works:
NSBezierPath* path = [ NSBezierPath bezierPathWithRoundedRect: dirtyRect xRadius: 5 yRadius: 5 ];
dirtyRect = NSInsetRect( dirtyRect, 2, 2 );
[ path appendBezierPathWithRoundedRect: dirtyRect xRadius: 3 yRadius: 3 ];
[ path setWindingRule: NSEvenOddWindingRule ];
/* Finally fill the line with the gradient */
NSGradient *lineGradient = [[NSGradient alloc] initWithStartingColor:[NSColor redColor] endingColor:[NSColor greenColor]];
[lineGradient drawInBezierPath:path angle:-90];
The NSEvenOddWindingRule allows you to punch a hole in the path that does not get drawn. To achieve the final effect you would change the gradient to go from semi transparent white to fully transparent white with the desired endpoints calculated based on the size of the rect. For example, this is close to your original image:
NSBezierPath* path = [ NSBezierPath bezierPathWithRoundedRect: dirtyRect xRadius: 5 yRadius: 5 ];
dirtyRect = NSInsetRect( dirtyRect, 2, 2 );
[ path appendBezierPathWithRoundedRect: dirtyRect xRadius: 3 yRadius: 3 ];
[ path setWindingRule: NSEvenOddWindingRule ];
/* Finally fill the line with the gradient */
CGFloat heightOfHilight = 8;
CGFloat endLocation = heightOfHilight / dirtyRect.size.height;
NSGradient *lineGradient = [[NSGradient alloc] initWithColorsAndLocations:
[NSColor colorWithCalibratedWhite: 1.0 alpha: 0.5], 0.0,
[NSColor colorWithCalibratedWhite: 1.0 alpha: 0.0], endLocation,
nil ];
[lineGradient drawInBezierPath:path angle:-90];
Problem
I have been playing around with a custom view that looks a like the patch object from Quartz composer. My fake Quartz Composer view looks nice enough. It is a single view with 2 different linear gradients, a shadow, and a single highlight line at the top, I would like to extend this to have rounded corners. This is very simple for the gradients etc, however, I am unsure how to draw a curved highlight along the top of the view. For example, see the real Quartz Composer UI, I have been trying to use an approach based around `CGContextReplacePathWithStrokedPath()`. My goal is to draw the curved path (the top highlight), convert the path to a "stroked path", then fill the resultant path with a gradient. However, this approach (at least my code) doesn't actually draw anything when I attempt this. Some example code to show the method. Note that the `CGPathCallback` function comes from the NSBezierPath+MCAdditions (I cannot find the original authors of the code, but if you search for it turns up in many places). ``` - (void)drawRect:(NSRect)dirtyRect { /* Make any line you like */ CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; CGMutablePathRef centerline = CGPathCreateMutable(); CGContextSetLineWidth(context, 10.); CGPathMoveToPoint(centerline, NULL, NSMinX(dirtyRect), NSMidY(dirtyRect)); CGPathAddLineToPoint(centerline, NULL, NSMaxX(dirtyRect), NSMidY(dirtyRect)); CGContextAddPath(context, centerline); /* Make a line "fill-able" */ CGContextReplacePathWithStrokedPath(context); /* Clip to this line (maybe not required?)*/ CGContextClip(context); /* Convert to a NSBezierPath */ NSBezierPath *centrePath = [NSBezierPath bezierPath]; CGPathApply(centerline, (__bridge void *)(centrePath), CGPathCallback); /* Finally fill the line with the gradient */ NSGradient *lineGradient = [[NSGradient alloc] initWithStartingColor:[NSColor redColor] endingColor:[NSColor greenColor]]; [lineGradient drawInBezierPath:centrePath angle:-90]; } ``` Q. What's the best way to draw a highlight like this along a curve, would you choose this approach (that doesn't actually work!)?