2012-02-25 17 views
6

Tworzę reflektor, który porusza się nad treścią w mojej aplikacji, tak jak poniżej:Tworzenie animatable półprzezroczystą nakładkę z rdzeniem warstw animacji

Spotlight ASpotlight B

W aplikacji próbki (pokazany powyżej), w tle warstwa jest niebieska i mam nad nią warstwę, która przyciemnia wszystko, oprócz okręgu, który pokazuje ją normalnie. Mam to działa (możesz zobaczyć, jak w poniższym kodzie). W mojej prawdziwej aplikacji jest obecna treść innych CALayerów, a nie tylko niebieski.

Oto mój problem: nie animuje. Używam rysunku CGContext, aby utworzyć okrąg (który jest pustym miejscem w warstwie w przeciwnym wypadku). Po kliknięciu przycisku w mojej przykładowej aplikacji rysuję okrąg w innym rozmiarze w innym miejscu.

Chciałbym to płynnie przetłumaczyć i przeskalować, zamiast skakać, tak jak obecnie. Może wymagać innej metody tworzenia efektu reflektora lub może być sposób, w jaki nie wiem, aby domyślnie animować wywołanie -drawLayer:inContext:.

Łatwo jest stworzyć aplikację próbki:

  1. utworzyć nową aplikację kakao (za pomocą ARC)
  2. Dodaj ramy Quartz
  3. upuść widok niestandardowy i przycisk na XIB
  4. Połącz niestandardowy widok z nową klasą (SpotlightView), z kodem podanym poniżej:
  5. Usuń SpotlightView.h, ponieważ zawarłem jego zawartość w SpotlightView.m
  6. Ustaw wylot przycisk do działania -moveSpotlight:

Update (własność mask)

Lubię propozycja Davida Rönnqvist w komentarzach do korzystania z własności ciemnej warstwy mask wyciąć otwór, który Mógłbym wtedy poruszać się samodzielnie. Problem polega na tym, że z jakiegoś powodu właściwość mask działa odwrotnie, niż oczekuję od maski. Gdy podaję okrągłą maskę, wszystko, co się pojawi, to okrąg. Spodziewałem się, że maska ​​zadziała w odwrotny sposób, zamaskowując obszar za pomocą 0 alfa.

Maskowanie to właściwa metoda, ale jeśli muszę wypełnić całą warstwę i wyciąć otwór, to mogę zrobić to tak, jak pierwotnie napisałem. Czy ktokolwiek wie, jak odwrócić właściwość -[CALayer mask], aby wycięty obszar został wycięty z obrazu warstwy?

/Aktualizacja

Oto kod dla 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 
+0

Czy próbowałeś ustawienie skali i tłumaczyć przekształcić na ciemną warstwę reflektor? –

+0

@ DavidRönnqvist To nie zadziała, ponieważ warstwa z reflektorem jest wielkości całej "superlayer" – Dov

+0

Czy reflektor może być maską do ciemnej warstwy? Możesz narysować reflektor w swojej własnej warstwie i ustawić jako maskę: właściwość na ciemnej warstwie. W ten sposób mógłbym animować rozmiar i pozycję reflektora, niezależnie od wielkości i położenia ciemnej warstwy. Jeszcze ich nie wypróbowałem. –

Odpowiedz

6

końcu dostał. To, co skłoniło mnie do odpowiedzi, to wiadomość o klasie CAShapeLayer. Początkowo myślałem, że będzie to prostszy sposób narysowania zawartości warstwy, niż rysowanie i czyszczenie zawartości standardowej CALayer.Ale przeczytałem dokumentację właściwości path z CAShapeLayer, która stwierdziła, że ​​może być animowana, ale nie w sposób dorozumiany.

Gdy maska ​​warstwy może być bardziej intuicyjny i elegancki, to nie wydaje się możliwe, aby użyć maski do hide porcję warstwy właściciela, zamiast wykazujące część, a więc nie mógł z niego korzystać. Cieszę się z tego rozwiązania, ponieważ jest całkiem jasne, o co chodzi. Szkoda, że ​​nie użył niejawnej animacji, ale kod animacji to tylko kilka linii.

Poniżej zmodyfikowałem kod przykładowy z pytania, aby dodać płynną animację. (usunąłem kod CIFilter, ponieważ był obcy. Rozwiązanie ma nadal pracować z filtrami.)

// 
// SpotlightView.m 
// 

#import <Quartz/Quartz.h> 

@interface SpotlightView : NSView 
- (IBAction)moveSpotlight:(id)sender; 
@end 

@interface SpotlightView() 
@property (strong) CAShapeLayer *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 = [CAShapeLayer layer]; 
     self.spotlightLayer.frame = self.layer.bounds; 
     self.spotlightLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable; 
     self.spotlightLayer.fillRule = kCAFillRuleEvenOdd; 

     CGColorRef blackoutColor = CGColorCreateGenericGray(0.0, 0.60); 
     self.spotlightLayer.fillColor = blackoutColor; 
     CGColorRelease(blackoutColor); 

     [self.layer addSublayer:self.spotlightLayer]; 
    } 

    return self; 
} 

- (void)drawRect:(NSRect)dirtyRect {} 

- (CGPathRef)newSpotlightPathInRect:(CGRect)containerRect 
         withHighlight:(CGRect)spotlightRect { 
    CGMutablePathRef shape = CGPathCreateMutable(); 

    CGPathAddRect(shape, NULL, containerRect); 

    if (!CGRectIsNull(spotlightRect)) { 
     CGPathAddEllipseInRect(shape, NULL, spotlightRect); 
    } 

    return shape; 
} 

- (void)moveSpotlight { 
    CGPathRef toShape = [self newSpotlightPathInRect:self.spotlightLayer.bounds 
             withHighlight:self.highlightRect]; 

    CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; 
    pathAnimation.fromValue = (__bridge id)self.spotlightLayer.path; 
    pathAnimation.toValue = (__bridge id)toShape; 

    [self.spotlightLayer addAnimation:pathAnimation forKey:@"path"]; 
    self.spotlightLayer.path = toShape; 

    CGPathRelease(toShape); 
} 

- (void)moveSpotlight:(id)sender { 
    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); 
    } 

    [self moveSpotlight]; 
} 

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx { 
    CGColorRef blueColor = CGColorCreateGenericRGB(0, 0, 1.0, 1.0); 
    CGContextSetFillColorWithColor(ctx, blueColor); 
    CGContextFillRect(ctx, layer.bounds); 
    CGColorRelease(blueColor); 
} 

@end 
+0

Wielkie dzięki, to naprawdę pomogło! Potrzebowałem czegoś, co sprawiałoby, że segment podzielony na segmenty wydawał się kurczyć, nie zmniejszając się. Teraz mogę to zrobić. – Jelle

Powiązane problemy