我正在实现一个自定义的NSMenuItem视图,shows a highlight当用户将鼠标悬停在该视图上时。为此,代码在将NSRectFill
设置为活动颜色后调用[NSColor selectedMenuItemColor]
。但是,我注意到结果是而不是只是一种纯色 - 它实际上是绘制了一个渐变。非常好,但想知道这个“神奇”是如何工作的 - 即如果我想定义自己的颜色而不仅仅是画出固体,我会怎样?
答案 0 :(得分:2)
我不知道这个实际是如何工作的,但我找到了一种用自定义渐变(或任何其他绘图操作)复制行为的方法。 “技巧”是使用CGPatternRef
,它允许您指定用于绘制模式的回调函数。通常,此回调函数会绘制模式的一个“单元”,但您可以指定一个非常大的模式大小(例如CGFLOAT_MAX
),以便能够在一次回调调用中填充整个区域。
为了演示该技术,这里是NSColor
上的一个类别,允许您从NSGradient
创建颜色。当你set
那种颜色,然后用它填充一个区域时,绘制渐变(线性,从下到上,但你可以很容易地改变它)。这甚至适用于抚摸路径或填充非矩形路径,例如[[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(0, 0, 100, 100)] fill]
,因为NSBezierPath
会自动剪切绘图。
//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
用法示例:
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];
结果:
答案 1 :(得分:0)
我最好的猜测是,它被定义为某种pattern image,但这并不能完全回答我的问题,因为看起来这些模式通常会被平铺而不是拉伸。
Apple工程师post on cocoa-dev证实了这一点:
[[NSColor selectedMenuItemColor] set]; NSRectFill(someRect);
这是有效的,因为selectedMenuItemColor是一个发生的模式 绘制渐变。您可以轻松地使用模式[...]
绘制几乎任何东西
他没有详细说明如何绘制拉伸这些图案而不是平铺,但是,突出显示的菜单项背景是。该帖子中的另一篇帖子声称它是“绘图代码中的special-case”,但他可能只是在猜测。