用于 OS X 应用程序的带有屏蔽 CIFilter 的 NSView

Posted

技术标签:

【中文标题】用于 OS X 应用程序的带有屏蔽 CIFilter 的 NSView【英文标题】:NSView with masked CIFilter for OS X app 【发布时间】:2015-08-13 14:02:24 【问题描述】:

我正在开发一个应用程序,其中包含许多正在移动的自定义 NSView 对象。我已经为自定义 NSView 子类之一实现了高斯模糊背景过滤器,如下所示:

- (id)init 
    self = [super init];
    if (self) 

        ...

        CIFilter *saturationFilter = [CIFilter filterWithName:@"CIColorControls"];
        [saturationFilter setDefaults];
        [saturationFilter setValue:@.5 forKey:@"inputSaturation"];

        CIFilter *blurFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
        [blurFilter setDefaults];
        [blurFilter setValue:@2.0 forKey:@"inputRadius"];

        self.wantsLayer = YES;
        self.layer.backgroundColor = [NSColor clearColor].CGColor;
        self.layer.masksToBounds = YES;
        self.layer.needsDisplayOnBoundsChange = YES;
        self.layerUsesCoreImageFilters = YES;

        [self updateFrame]; //this is where the frame size is set

        self.layer.backgroundFilters = @[saturationFilter, blurFilter];

        ...

        return self;
    
    else return nil;

这很有效,可以在视图的整个内容中创建高斯模糊效果。问题是我不希望高斯模糊覆盖整个视图。在 NSView 的实际大小与其内容框的绘制之间大约有一个(有意的)12px 填充:

- (void)drawRect:(NSRect)dirtyRect 
    [super drawRect:dirtyRect];

    NSColor* strokeColor = [NSColor colorWithRed:.5 green:.8 blue:1 alpha:1];
    NSColor* fillColor = [NSColor colorWithRed:.5 green:.8 blue:1 alpha:.2];

    ...

    [strokeColor setStroke];
    [fillColor setFill];
    NSBezierPath *box = [NSBezierPath bezierPathWithRoundedRect:NSMakeRect(self.bounds.origin.x + 12, self.bounds.origin.y + 12, self.bounds.size.width - 24, self.bounds.size.height - 24) xRadius:6 yRadius:6];
    box.lineWidth = 6;
    [box stroke];
    [box fill];

    ...

这种填充的原因是有一些 GUI 部分位于该区域并被无缝地绘制到包含框中。我想掩盖模糊效果仅对绘制框的内部产生影响,而不是对整个视图产生影响。这是我尝试过的。

尝试 1:创建子层

我在 NSView 中用适当大小的框架创建了一个子层,并将模糊效果添加到该子层。问题:模糊效果似乎只适用于直接父层,所以不是模糊NSView后面的内容,而是模糊NSView的self.layer(基本上是空的)的内容。

尝试 2:创建遮罩层

我尝试创建一个遮罩层并将其设置为 self.layer.mask。但是,由于 GUI 内容的位置确实发生了变化(通过 DrawRect 函数),我需要获取当前图层的副本以用作遮罩图层。我尝试了以下代码,但没有效果。

self.layer.mask = nil;
NSArray *bgFilters = self.layer.backgroundFilters;
self.layer.backgroundFilters = nil;
CALayer *maskingLayer = self.layer.presentationLayer;
self.layer.mask = maskingLayer;
self.layer.backgroundFilters = bgFilters;

尝试 3:直接绘制遮罩层

我找不到任何关于如何直接在图层上绘制的示例。我不能使用静态 UIImage 来做桅杆,因为正如我上面所说,掩码必须随着用户交互而改变。我正在寻找与 DrawRect 函数等效的东西。任何帮助将不胜感激。

所以...

在我看来,子层方式将是最好和最简单的方式,如果我能弄清楚如何将模糊效果的优先级更改为 NSView 后面的背景而不是 NSView 后面的背景层子层。

【问题讨论】:

【参考方案1】:

好吧,我仍然想知道是否有更优雅的方法,但我找到了一个可行的解决方案。基本上,我已经从一个从 drawRect 函数的修改版本绘制的 NSImage 创建了一个遮罩层:

- (id)init 
    self = [super init];
    if (self) 

        // SETUP VIEW SAME AS ABOVE 

        CALayer *maskLayer = [CALayer layer];
        maskLayer.contents = [NSImage imageWithSize:self.frame.size flipped:YES drawingHandler:^BOOL(NSRect dstRect) 
            [self drawMask:self.bounds];
            return YES;
        ];
        maskLayer.frame = self.bounds;
        self.layer.mask = maskLayer;

        return self;
    
    else return nil;



- (void)drawMask:(NSRect)dirtyRect 
    [[NSColor clearColor] set];
NSRectFill(self.bounds);
    [[NSColor blackColor] set]; 

    // SAME DRAWING CODE AS drawRect 
    // EXCEPT EVERYTHING IS SOLID BLACK (NO ALPHA TRANSPARENCY) 
    // AND ONLY NEED TO DRAW PARTS THAT EFFECT THE EXTERNAL BOUNDARIES

【讨论】:

以上是关于用于 OS X 应用程序的带有屏蔽 CIFilter 的 NSView的主要内容,如果未能解决你的问题,请参考以下文章

Jenkins 和 OS X 服务器在同一台机器上

总线错误:Mac OS X 上带有 GCC 的内联 x86 程序集

如何分发带有依赖库的 Mac OS X?

带有 iOS 6 SDK 的 Xcode 5:“UIAccelerometer”不可用:在 OS X 上不可用

Xcode 7 UITesting 用于 OS X 上的菜单栏应用程序

试图从带有applescript的os x消息接收消息