+[NSColor selectedMenuItemColor] 如何神奇地绘制渐变? [英] How does +[NSColor selectedMenuItemColor] magically draw a gradient?

查看:22
本文介绍了+[NSColor selectedMenuItemColor] 如何神奇地绘制渐变?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在实施一个自定义 NSMenuItem 视图,该视图显示亮点 当用户将鼠标悬停在它上面时.为此,代码在将 [NSColor selectedMenuItemColor] 设置为活动颜色后调用 NSRectFill.但是,我注意到结果只是一个纯色——它实际上绘制了一个渐变.非常好,但想知道这种魔法"是如何工作的——也就是说,如果我想定义自己的颜色,而不仅仅是绘制纯色,我该怎么做?

I'm implementing a custom NSMenuItem view that shows a highlight as the user mouses over it. To do this, the code calls NSRectFill after setting [NSColor selectedMenuItemColor] as the active color. However, I noticed that the result is not simply a solid color — it actually draws a gradient instead. Very nice, but wondering how this "magic" works — i.e. if I wanted to define my own color that didn't just draw solid, how would I?

推荐答案

我不知道这实际上是如何工作的,但我找到了一种使用自定义渐变(或任何其他绘图操作).技巧"是使用 CGPatternRef,它允许您指定用于绘制图案的回调函数.通常,此回调函数绘制模式的一个单元格",但您可以只指定一个非常大的模式大小(例如 CGFLOAT_MAX),以便能够在一次回调调用中填充整个区域.

I don't know how this actually works, but I found a way to replicate the behavior with custom gradients (or any other drawing operations). The "trick" is to use a CGPatternRef, which allows you to specify a callback function for drawing the pattern. Normally, this callback function draws one "cell" of the pattern, but you can just specify a very large pattern size (e.g. CGFLOAT_MAX) to be able to fill the entire area in one invocation of the callback.

为了演示该技术,这里是 NSColor 上的一个类别,它允许您从 NSGradient 创建颜色.当你设置那个颜色然后用它来填充一个区域时,渐变被绘制(线性,从下到上,但你可以很容易地改变它).这甚至适用于描边路径或填充非矩形路径,例如 [[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(0, 0, 100, 100)] fill] 因为 NSBezierPath 自动剪辑绘图.

To demonstrate the technique, here's a category on NSColor that allows you to create a color from an NSGradient. When you set that color and then use it to fill an area, the gradient is drawn (linear, from bottom to top, but you can easily change that). This even works for stroking paths or filling non-rectangular paths, like [[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(0, 0, 100, 100)] fill] because NSBezierPath automatically clips the drawing.

//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];

结果:

这篇关于+[NSColor selectedMenuItemColor] 如何神奇地绘制渐变?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆