Создание анимируемого полупрозрачного наложения со слоями Core Animation

Я создаю прожектор, который перемещается по содержимому в моем приложении, вот так:

Spotlight A Spotlight B

образец приложения (показано выше), фоновый слой синий, и у меня есть слой над ним, который затемняет все, кроме круга, который показывает это нормально. У меня это работает (вы можете увидеть, как это сделать в коде ниже). В моем реальном приложении есть реальный контент в других CALayers, а не только в синем.

Вот моя проблема: он не анимируется. Я использую рисунок CGContext для создания круга (который представляет собой пустое место на черном слое). Когда вы нажимаете кнопку в моем примере приложения, я рисую круг другого размера в другом месте.

Я бы хотел, чтобы это плавно переводилось и масштабировалось, а не прыгало, как сейчас.Может потребоваться другой метод создания эффекта прожектора, или может быть способ, о котором я не знаю, для неявной анимации вызова -drawLayer: inContext: .

Легко создать пример приложения:

  1. Создать новое приложение Cocoa (используя ARC)
  2. Добавить платформу Quartz
  3. Перетащить настраиваемое представление и кнопку в XIB
  4. Установить ссылку на настраиваемое представление в новый класс (SpotlightView) с кодом, приведенным ниже
  5. Удалить SpotlightView.h , поскольку я включил его содержимое в SpotlightView.m
  6. . Установите выход кнопки на -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
6
задан Dov 27 February 2012 в 02:49
поделиться