Estoy creando un foco de luz que se mueve sobre el contenido en mi aplicación, así:Creación de una cubierta traslúcida animatable con capas Core Animation
En la aplicación de ejemplo (mostrado arriba), el fondo la capa es azul, y tengo una capa sobre ella que oscurece todo, excepto un círculo que lo muestra normalmente. Tengo esto funcionando (se puede ver cómo en el código a continuación). En mi aplicación real, hay contenido real en otros CALayers, en lugar de solo azul.
Aquí está mi problema: no anima. Estoy usando el dibujo CGContext
para crear el círculo (que es una mancha vacía en una capa que de otro modo sería negra). Cuando haces clic en el botón de mi aplicación de muestra, dibujo el círculo con un tamaño diferente en una ubicación diferente.
Me gustaría que se traduzca y escale suavemente, en lugar de saltar, como lo hace actualmente. Puede requerir un método diferente para crear el efecto de foco, o podría haber una forma que no sé para animar implícitamente la llamada -drawLayer:inContext:
.
Es fácil para crear la aplicación de ejemplo:
- hacer una nueva aplicación del Cacao (usando ARC)
- Añadir el marco de cuarzo
- gota una vista personalizada y un botón en el XI ter
- Vincular la vista personalizada a una nueva clase (SpotlightView), con el código proporcionado a continuación
- Eliminar
SpotlightView.h
, ya que incluí su contenido en SpotlightView.m - Establecer la salida del botón para la acción
-moveSpotlight:
actualización (la propiedad mask
)
me gusta la sugerencia de David Rönnqvist en los comentarios utilizar la propiedad mask
de la capa oscura que cortar un agujero, que Podría moverme de forma independiente. El problema es que, por alguna razón, la propiedad mask
funciona de forma opuesta a como espero que funcione una máscara. Cuando especifico una máscara circular, todo lo que aparece es el círculo. Esperaba que la máscara funcionara de la manera opuesta, enmascarando el área con 0
alpha.
Enmascarar se siente como la manera correcta de hacerlo, pero si tengo que rellenar toda la capa y hacer un agujero, puedo hacerlo de la forma en que originalmente lo hice. ¿Alguien sabe cómo invertir la propiedad -[CALayer mask]
, para que el área dibujada se recorte de la imagen de la capa?
/actualización
Aquí está el código para 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
¿Ha intentado fijar una escala y traducir a transformar en la capa oscura centro de atención? –
@ DavidRönnqvist Eso no funcionará, porque la capa con el foco es del tamaño de todo el 'superlayer' – Dov
¿Podría el foco ser la máscara de la capa oscura? Puede dibujar el reflector en su propia capa y establecerlo como la propiedad -mask: en la capa oscura. De esa manera, creo que puedes animar el tamaño y la posición del reflector independientemente del tamaño y la posición de la capa oscura ... aunque aún no lo hemos probado. –