带有用于 OS X 应用程序的屏蔽 CIFilter 的 NSView
NSView with masked CIFilter for OS X app
我正在开发一个包含大量移动的自定义 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 的背景层在子层后面。
好吧,我仍然想知道是否有更优雅的方法,但我已经找到了可行的解决方案。基本上,我已经从 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
}
我正在开发一个包含大量移动的自定义 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 的背景层在子层后面。
好吧,我仍然想知道是否有更优雅的方法,但我已经找到了可行的解决方案。基本上,我已经从 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
}