【发布时间】:2012-03-15 18:53:30
【问题描述】:
我正在创建一个在我的应用中移动内容的聚光灯,如下所示:
在示例应用程序(如上所示)中,背景层是蓝色的,我在它上面有一个层,除了一个正常显示它的圆圈之外,它的所有部分都变暗了。我已经完成了这个工作(你可以在下面的代码中看到如何)。在我的真实应用中,其他 CALayer 中有实际内容,而不仅仅是蓝色。
这是我的问题:它没有动画。我正在使用CGContext 绘图来创建圆圈(这是一个黑色层中的空白点)。当您单击示例应用中的按钮时,我会在不同的位置以不同的大小绘制圆。
我希望它能够平滑地翻译和缩放,而不是像现在那样跳跃。它可能需要一种不同的方法来创建聚光灯效果,或者可能有一种我不知道的方法来隐式地为 -drawLayer:inContext: 调用设置动画。
创建示例应用很容易:
- 制作新的 Cocoa 应用(使用 ARC)
- 添加 Quartz 框架
- 将自定义视图和按钮拖放到 XIB 上
- 使用下面提供的代码将自定义视图链接到新类 (SpotlightView)
- 删除
SpotlightView.h,因为我将其内容包含在 SpotlightView.m 中 - 将按钮的出口设置为
-moveSpotlight:操作
更新(mask 属性)
我喜欢 David Rönnqvist 在 cmets 中的建议,即使用深色图层的 mask 属性切出一个洞,然后我可以独立移动该洞。问题是由于某种原因,mask 属性的工作方式与我期望掩码的工作方式相反。当我指定一个圆形蒙版时,显示的只是圆圈。我希望掩码以相反的方式工作,用0 alpha 掩盖该区域。
蒙版感觉是解决这个问题的正确方法,但如果我必须填充整个图层并切出一个洞,那么我不妨按照我最初发布的方式进行。有谁知道如何反转-[CALayer mask] 属性,以便绘制的区域从图层图像中删除?
/更新
这是SpotlightView的代码:
//
// SpotlightView.m
//
#import <Quartz/Quartz.h>
@interface SpotlightView : NSView
- (IBAction)moveSpotlight:(id)sender;
@end
@interface SpotlightView ()
@property (strong) CALayer *spotlightLayer;
@property (assign) CGRect highlightRect;
@end
@implementation SpotlightView
@synthesize spotlightLayer;
@synthesize highlightRect;
- (id)initWithFrame:(NSRect)frame {
if ((self = [super initWithFrame:frame])) {
self.wantsLayer = YES;
self.highlightRect = CGRectNull;
self.spotlightLayer = [CALayer layer];
self.spotlightLayer.frame = CGRectInset(self.layer.bounds, -50, -50);
self.spotlightLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
self.spotlightLayer.opacity = 0.60;
self.spotlightLayer.delegate = self;
CIFilter *blurFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
[blurFilter setValue:[NSNumber numberWithFloat:5.0]
forKey:@"inputRadius"];
self.spotlightLayer.filters = [NSArray arrayWithObject:blurFilter];
[self.layer addSublayer:self.spotlightLayer];
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect {}
- (void)moveSpotlight:(id)sender {
[self.spotlightLayer setNeedsDisplay];
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
if (layer == self.spotlightLayer) {
CGContextSaveGState(ctx);
CGColorRef blackColor = CGColorCreateGenericGray(0.0, 1.0);
CGContextSetFillColorWithColor(ctx, blackColor);
CGColorRelease(blackColor);
CGContextClearRect(ctx, layer.bounds);
CGContextFillRect(ctx, layer.bounds);
// Causes the toggling
if (CGRectIsNull(self.highlightRect) || self.highlightRect.origin.x != 25) {
self.highlightRect = CGRectMake(25, 25, 100, 100);
} else {
self.highlightRect = CGRectMake(NSMaxX(self.layer.bounds) - 50,
NSMaxY(self.layer.bounds) - 50,
25, 25);
}
CGRect drawnRect = [layer convertRect:self.highlightRect
fromLayer:self.layer];
CGMutablePathRef highlightPath = CGPathCreateMutable();
CGPathAddEllipseInRect(highlightPath, NULL, drawnRect);
CGContextAddPath(ctx, highlightPath);
CGContextSetBlendMode(ctx, kCGBlendModeClear);
CGContextFillPath(ctx);
CGPathRelease(highlightPath);
CGContextRestoreGState(ctx);
}
else {
CGColorRef blueColor = CGColorCreateGenericRGB(0, 0, 1.0, 1.0);
CGContextSetFillColorWithColor(ctx, blueColor);
CGContextFillRect(ctx, layer.bounds);
CGColorRelease(blueColor);
}
}
@end
【问题讨论】:
-
您是否尝试过在暗聚光灯图层上设置比例和平移变换?
-
@DavidRönnqvist 这不行,因为聚光灯的层是整个
superlayer的大小 -
聚光灯可以是暗层的遮罩吗?您可以将聚光灯绘制到它自己的图层中并将其设置为 -mask: 暗图层上的属性。这样一来,我认为您可以为聚光灯的大小和位置设置动画,而不管暗层的大小和位置如何。虽然还没有尝试过。
-
@DavidRönnqvist 这太令人沮丧了,除了我需要反转掩码,而且我没有看到一个简单的方法来做到这一点。如果我必须将整个遮罩层涂黑并在其中切一个洞,那么我又回到了开始的地方。
-
如果它是一个选项,您可以获得背景的图像表示(上面的蓝色)并在黑暗层上再次绘制它并使用蒙版将其限制为聚光灯。这样您就可以为蒙版制作动画,但只有在下面的内容相当静态时才会起作用
标签: macos cocoa drawing core-animation calayer