Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 如何在NSButtons中绘制类似NSImage的图像(具有深度)?_Objective C_Cocoa_Draw_Nsimage - Fatal编程技术网

Objective c 如何在NSButtons中绘制类似NSImage的图像(具有深度)?

Objective c 如何在NSButtons中绘制类似NSImage的图像(具有深度)?,objective-c,cocoa,draw,nsimage,Objective C,Cocoa,Draw,Nsimage,有没有办法在NSButtons或其他cocoa界面元素中绘制类似NSImage的图像 以下是一些例子: 苹果使用带有黑色图标的pdf: 如果您只是想在按钮中使用自己的图像时应用此效果,请使用[myImage setTemplate:YES]。除了具有屏幕截图中显示的样式的按钮之外,没有内置的方法来绘制具有这种效果的图像 但是,您可以使用核心图形复制效果。如果仔细观察,效果由水平渐变、白色水滴阴影和黑色内部阴影组成(后者是最困难的) 您可以在NSImage中将其作为一个类别来实现: //NSIm

有没有办法在NSButtons或其他cocoa界面元素中绘制类似NSImage的图像

以下是一些例子:

苹果使用带有黑色图标的pdf:

如果您只是想在按钮中使用自己的图像时应用此效果,请使用
[myImage setTemplate:YES]
。除了具有屏幕截图中显示的样式的按钮之外,没有内置的方法来绘制具有这种效果的图像

但是,您可以使用核心图形复制效果。如果仔细观察,效果由水平渐变、白色水滴阴影和黑色内部阴影组成(后者是最困难的)

您可以在
NSImage
中将其作为一个类别来实现:

//NSImage+EtchedDrawing.h:
@interface NSImage (EtchedImageDrawing)    
- (void)drawEtchedInRect:(NSRect)rect;
@end

//NSImage+EtchedDrawing.m:
@implementation NSImage (EtchedImageDrawing)

- (void)drawEtchedInRect:(NSRect)rect
{
    NSSize size = rect.size;
    CGFloat dropShadowOffsetY = size.width <= 64.0 ? -1.0 : -2.0;
    CGFloat innerShadowBlurRadius = size.width <= 32.0 ? 1.0 : 4.0;

    CGContextRef c = [[NSGraphicsContext currentContext] graphicsPort];

    //save the current graphics state
    CGContextSaveGState(c);

    //Create mask image:
    NSRect maskRect = rect;
    CGImageRef maskImage = [self CGImageForProposedRect:&maskRect context:[NSGraphicsContext currentContext] hints:nil];

    //Draw image and white drop shadow:
    CGContextSetShadowWithColor(c, CGSizeMake(0, dropShadowOffsetY), 0, CGColorGetConstantColor(kCGColorWhite));
    [self drawInRect:maskRect fromRect:NSMakeRect(0, 0, self.size.width, self.size.height) operation:NSCompositeSourceOver fraction:1.0];

    //Clip drawing to mask:
    CGContextClipToMask(c, NSRectToCGRect(maskRect), maskImage);

    //Draw gradient:
    NSGradient *gradient = [[[NSGradient alloc] initWithStartingColor:[NSColor colorWithDeviceWhite:0.5 alpha:1.0]
                                                          endingColor:[NSColor colorWithDeviceWhite:0.25 alpha:1.0]] autorelease];
    [gradient drawInRect:maskRect angle:90.0];
    CGContextSetShadowWithColor(c, CGSizeMake(0, -1), innerShadowBlurRadius, CGColorGetConstantColor(kCGColorBlack));

    //Draw inner shadow with inverted mask:
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef maskContext = CGBitmapContextCreate(NULL, CGImageGetWidth(maskImage), CGImageGetHeight(maskImage), 8, CGImageGetWidth(maskImage) * 4, colorSpace, kCGImageAlphaPremultipliedLast);
    CGColorSpaceRelease(colorSpace);
    CGContextSetBlendMode(maskContext, kCGBlendModeXOR);
    CGContextDrawImage(maskContext, maskRect, maskImage);
    CGContextSetRGBFillColor(maskContext, 1.0, 1.0, 1.0, 1.0);
    CGContextFillRect(maskContext, maskRect);
    CGImageRef invertedMaskImage = CGBitmapContextCreateImage(maskContext);
    CGContextDrawImage(c, maskRect, invertedMaskImage);
    CGImageRelease(invertedMaskImage);
    CGContextRelease(maskContext);

    //restore the graphics state
    CGContextRestoreGState(c);
}

@end
这将为您提供以下结果(以不同的大小显示):


您可能需要对两个阴影的渐变颜色和偏移/模糊半径进行一些实验,以接近原始效果。

要在任何rect内正确绘制,内部遮罩的
CGContextDrawImage
CGContextFillRect
必须具有
(0,0)
的原点。然后,当您为内部阴影绘制图像时,您可以重用mask rect。所以最后看起来像:

CGRect cgRect = CGRectMake( 0, 0, maskRect.size.width, maskRect.size.height );    
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef maskContext = CGBitmapContextCreate( NULL, CGImageGetWidth( maskImage ), CGImageGetHeight( maskImage ), 8, CGImageGetWidth( maskImage ) * 4, colorSpace, kCGImageAlphaPremultipliedLast );
CGColorSpaceRelease( colorSpace );
CGContextSetBlendMode( maskContext , kCGBlendModeXOR );
CGContextDrawImage( maskContext, cgRect, maskImage );
CGContextSetRGBFillColor( maskContext, 1.0, 1.0, 1.0, 1.0 );
CGContextFillRect( maskContext, cgRect );
CGImageRef invertedMaskImage = CGBitmapContextCreateImage( maskContext );

CGContextDrawImage( context, maskRect, invertedMaskImage );
CGImageRelease( invertedMaskImage );
CGContextRelease( maskContext );
CGContextRestoreGState( context );

您还必须在图像外部保留1px边框,否则阴影将无法正常工作。

如果您不介意调用私有API,可以让操作系统(CoreUI)为您进行着色。您需要一些声明:

typedef CFTypeRef CUIRendererRef;
extern void CUIDraw(CUIRendererRef renderer, CGRect frame, CGContextRef context, CFDictionaryRef object, CFDictionaryRef *result);

@interface NSWindow(CoreUIRendererPrivate)
+ (CUIRendererRef)coreUIRenderer;
@end
对于实际图纸:

CGRect drawRect = CGRectMake(x, y, width, height);
CGImageRef cgimage = your_image;

CFDictionaryRef dict = (CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
        @"backgroundTypeRaised", @"backgroundTypeKey",
        [NSNumber numberWithBool:YES], @"imageIsGrayscaleKey",
        cgimage, @"imageReferenceKey",
        @"normal", @"state",
        @"image", @"widget",
        [NSNumber numberWithBool:YES], @"is.flipped",
        nil];
CUIDraw ([NSWindow coreUIRenderer], drawRect, cg, dict, nil);
CGImageRelease (cgimage);
这将采用cgimage的alpha通道,并应用工具栏按钮上显示的浮雕效果。您可能需要也可能不需要“is.flipped”行。如果结果颠倒,请将其删除

有一系列的变化:

kCUIPresentationStateKey=kCUIPresentationStateInactive
:窗口未激活,图像将变亮

state=rollover
:仅对上一个选项有意义。这意味着您将鼠标悬停在图像上,窗口处于非活动状态,但按钮是敏感的(单击通过已启用)。天会变得更黑

state=pressed
:按下按钮时发生。图标会稍微变暗


额外提示:要找到像这样的东西,可以使用SIMBL插件。它打印出目标应用程序的所有CoreUI调用。如果您必须绘制自己的原生UI,这将是一个宝藏。

这里有一个更简单的解决方案:只需创建一个单元格并让它绘制。不要乱搞私有API或核心图形

代码可能类似于以下内容:

NSButtonCell *buttonCell = [[NSButtonCell alloc] initImageCell:image];
buttonCell.bordered = YES;
buttonCell.bezelStyle =  NSTexturedRoundedBezelStyle;
// additional configuration
[buttonCell drawInteriorWithFrame: someRect inView:self];
根据您想要的外观,您可以使用不同的单元格和配置(例如,如果您想要在选定的表视图行中显示反转的外观,则使用
NSImageCell
NSBackgroundStyleDark


作为奖励,它将自动在所有版本的OS X上显示正确。

我通过添加保存和恢复图形状态的调用编辑了您的代码。如果不这样做,调用此绘图方法后,当前上下文将处于不同的状态,这不是预期的行为。此外,将
模板
(大写t)放在图像名称的末尾将产生相同的效果,代码更少(只需在IB中设置名称)。我还注意到内存泄漏,
maskContext
从未发布过。现在是。我使用的代码与你上面发布的代码完全相同,但没有得到你得到的结果。图像本身和白色阴影绘制正确,但黑色内部阴影位于错误的位置。我能做什么?这是一个非常棒的答案。用
NSButtonCell
画画
NSButtonCell *buttonCell = [[NSButtonCell alloc] initImageCell:image];
buttonCell.bordered = YES;
buttonCell.bezelStyle =  NSTexturedRoundedBezelStyle;
// additional configuration
[buttonCell drawInteriorWithFrame: someRect inView:self];