I donβt know how this works, but I found a way to replicate the behavior using special gradients (or any other drawing operations). The βtrickβ is to use CGPatternRef , which allows you to specify a callback function to draw a template. Usually this callback function draws one βcellβ of the template, but you can simply specify a very large size of the picture (for example, CGFLOAT_MAX ) to be able to fill the entire area with one callback call.
To demonstrate the technique, here is the NSColor category, which allows you to create color from NSGradient . When you set color this color and then use it to fill the area, the gradient is drawn (linear, from bottom to top, but you can easily change this). This even works for stroking tracks or filling non-rectangular paths, for example [[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(0, 0, 100, 100)] fill] , because NSBezierPath automatically clamps the picture.
//NSColor+Gradient.h #import <Cocoa/Cocoa.h> @interface NSColor (Gradient) + (NSColor *)my_gradientColorWithGradient:(NSGradient *)gradient; @end //NSColor+Gradient.m #import "NSColor+Gradient.h" #import <objc/runtime.h> static void DrawGradientPattern(void * info, CGContextRef context) { NSGraphicsContext *currentContext = [NSGraphicsContext currentContext]; CGRect clipRect = CGContextGetClipBoundingBox(context); [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO]]; NSGradient *gradient = (__bridge NSGradient *)info; [gradient drawInRect:NSRectFromCGRect(clipRect) angle:90.0]; [NSGraphicsContext setCurrentContext:currentContext]; } @implementation NSColor (Gradient) + (NSColor *)my_gradientColorWithGradient:(NSGradient *)gradient { CGColorSpaceRef colorSpace = CGColorSpaceCreatePattern(NULL); CGPatternCallbacks callbacks; callbacks.drawPattern = &DrawGradientPattern; callbacks.releaseInfo = NULL; CGPatternRef pattern = CGPatternCreate((__bridge void *)(gradient), CGRectMake(0, 0, CGFLOAT_MAX, CGFLOAT_MAX), CGAffineTransformIdentity, CGFLOAT_MAX, CGFLOAT_MAX, kCGPatternTilingConstantSpacing, true, &callbacks); const CGFloat components[4] = {1.0, 1.0, 1.0, 1.0}; CGColorRef cgColor = CGColorCreateWithPattern(colorSpace, pattern, components); CGColorSpaceRelease(colorSpace); NSColor *color = [NSColor colorWithCGColor:cgColor]; objc_setAssociatedObject(color, "gradient", gradient, OBJC_ASSOCIATION_RETAIN); return color; } @end
Usage example:
NSArray *colors = @[ [NSColor redColor], [NSColor blueColor] ]; NSGradient *gradient = [[NSGradient alloc] initWithColors:colors]; NSColor *gradientColor = [NSColor my_gradientColorWithGradient:gradient]; [gradientColor set]; NSRectFill(NSMakeRect(0, 0, 100, 100)); [[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(100, 0, 100, 100)] fill];
Result:
