Я создаю прожектор, который перемещается по содержимому в моем приложении, вот так:
образец приложения (показано выше), фоновый слой синий, и у меня есть слой над ним, который затемняет все, кроме круга, который показывает это нормально. У меня это работает (вы можете увидеть, как это сделать в коде ниже). В моем реальном приложении есть реальный контент в других CALayers, а не только в синем.
Вот моя проблема: он не анимируется. Я использую рисунок CGContext
для создания круга (который представляет собой пустое место на черном слое). Когда вы нажимаете кнопку в моем примере приложения, я рисую круг другого размера в другом месте.
Я бы хотел, чтобы это плавно переводилось и масштабировалось, а не прыгало, как сейчас.Может потребоваться другой метод создания эффекта прожектора, или может быть способ, о котором я не знаю, для неявной анимации вызова -drawLayer: inContext:
.
Легко создать пример приложения:
SpotlightView.h
, поскольку я включил его содержимое в SpotlightView.m -moveSpotlight:
action Update (свойство mask
)
Мне нравится предложение Дэвида Рённквиста в комментариях использовать свойство mask
затемненного слоя для вырезания отверстия, который я мог затем перемещать самостоятельно. Проблема в том, что по какой-то причине свойство mask
работает противоположно тому, как я ожидаю, что маска будет работать. Когда я указываю круговую маску, появляется только круг. Я ожидал, что маска будет работать противоположным образом, маскируя область с 0
альфа.
Маскирование кажется правильным способом сделать это, но если мне нужно заполнить весь слой и вырезать отверстие, тогда я могу сделать это так, как я изначально писал. Кто-нибудь знает, как инвертировать свойство - [CALayer mask]
, чтобы нарисованная область вырезалась из изображения слоя?
/ Update
Вот код для 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