2010-05-31 3 views
6

Ich schreibe ein Spiel für Mac mit Cocoa. Ich implementiere derzeit Hit-Tests und habe herausgefunden, dass CALayer Hit-Tests anbietet, aber die Alpha-Eigenschaften nicht zu implementieren scheint. Da ich manchmal viele CALayer übereinander gestapelt habe, muss ich wirklich herausfinden, worauf der Benutzer wirklich klicken wollte.Hit Testing mit CALayer mit den Alpha-Eigenschaften des CALayer-Inhalts

Ich denke, wenn ich irgendwie ein Array bekommen könnte, die Zeiger auf alle CALayers enthält, die den Klickpunkt enthalten, könnte ich sie durch einige wie filtern. Aber der einzige Weg, die ich bisher habe das Array zu erstellen ist:

NSMutableArray* anArrayOfLayers = [NSMutableArray array]; 
    for (CALayer* aLayer in mapLayer.sublayers) 
    { 
     if ([aLayer containsPoint:mouseCoord]) 
      [anArrayOfLayers addObject:aLayer]; 
    } 

Dann sortieren Sie die Anordnung von der z-Werte des CALayer dann gehen Sie durch die Überprüfung, ob die Pixel an der Stelle ist alpha oder nicht. Zwischen dem Sort und dem Alpha-Check scheint dies jedoch ein unglaublicher Performance-Hog zu sein. (Wie würden Sie das Alpha überprüfen?)

Gibt es eine Möglichkeit, dies zu tun? Ich

Antwort

10

Etwas stolperte über, während mein Kopf über ein ähnliches Problem Kratzen ist, dass CALayer containsPoint: verwendet, wenn Sie es senden hitTest:

Sein Standardverhalten ist gegen Grenzen zu testen, aber wir können außer Kraft gesetzt und bekommen es die Alpha zu überprüfen Kanal, und nur die Verwendung CALayer in Hit-Tests gebaut, um den Rest zu handhaben:

- (BOOL) containsPoint:(CGPoint)p 
{ 
    return CGRectContainsPoint(self.bounds, p) && !ImagePointIsTransparent(self.contents, p)) return YES; 
} 

Es gibt eine Diskussion über Tests für ein alpha einzelnes Pixel bei Retrieving a pixel alpha value for a UIImage

T sein für meine Zwecke gearbeitet:

static BOOL ImagePointIsTransparent(CGImageRef image, CGPoint p) 
{ 
    uint8_t alpha; 

    CGContextRef context = CGBitmapContextCreate(&alpha, 1, 1, 8, 1, NULL, kCGImageAlphaOnly); 
    CGContextDrawImage(context, CGRectMake(-p.x, -p.y, CGImageGetWidth(image), CGImageGetHeight(image)), image); 
    CGContextRelease(context); 

    return alpha == 0; 
} 

(Wenn Sie renderInContext: verwenden eher auf die CALayer zu ziehen, als seinen Inhalt Eigenschaft festlegen, dann komplizierter sein, es geht. Dies könnte in diesem Fall nützlich sein: http://www.cimgf.com/2009/02/03/record-your-core-animation-animation/)

+0

Chris, das ist grandios! Frage an Sie: Wenn Sie den Punkt übergeben, scheint es, dass er seinen Ursprung in der unteren linken Ecke und der oberen rechten Ecke haben muss. Ich vermute, dass etwas passieren muss, bevor wir den Punkt hier durchgehen, richtig? (Abgesehen davon, dass ich die Bildhöhe verwende, um mich umzudrehen.) Ich versuche herauszufinden, wo der richtige Ort für diese Anpassung ist - in ImagePointIsTransparent oder im Gesten-/Berührungshandler. –

+0

Die Frage, die ich verlinkt habe, ist, was du fragst - Core Graphics und UIKit haben unterschiedliche Y-Achsen, also denke ich, dass du an der Schnittstelle zwischen den beiden konvertieren willst. Es gibt auch eine Diskussion über die Auswirkungen verschiedener Rendering-Möglichkeiten auf die Ebene (wiederholtes Rendering nur einmal pro Pixel), die Sie vielleicht auch ausprobieren möchten. –

+0

Chris, ich habe gerade festgestellt: Du schreibst eine Mac App. Ich schreibe eine iOS App. Es stellt sich heraus, dass bei Verwendung eines Retina-Displays dieser Test nicht funktioniert (auf dem iPhone 4 oder im Simulator im Retina-Modus). Der Quartz 2D-Programmierleitfaden empfiehlt, dass ich UIGraphicsBeginImageContextWithOptions vs. die niedrigeren Quartz-Funktionen verwende, also muss ich sehen, ob das mit dem CTM und so weiter hilft. (Dies könnte auch erklären, warum die Antwort "überall" auf Retina scheint.) Http://stackoverflow.com/questions/7506248/alpha-detection-in-layer-ok-on-simulator-not-iphone –