2013-06-11 14 views
23

Ich versuche CAShapeLayer zu verwenden, um ein CALayer in meinen iOS App zu maskieren, da es einen Bruchteil der CPU time nimmt ein Bild manuell zu maskieren vs man in einer Maskierung bitmap context;Leistungsprobleme, wenn viele CALayer Masken

Wenn ich mehrere Dutzend oder mehr Bilder übereinander geschichtet habe, ist die CAShapeLayer maskierte UIImageView langsam auf meine Berührung zu bewegen. Hier

ist einige Beispiel-Code:

UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"SomeImage.jpg" ofType:nil]]; 
CGMutablePathRef path = CGPathCreateMutable(); 
CGPathAddEllipseInRect(path, NULL, CGRectMake(0.f, 0.f, image.size.width * .25, image.size.height * .25)); 

for (int i = 0; i < 200; i++) { 

    SLTUIImageView *imageView = [[SLTUIImageView alloc]initWithImage:image]; 
    imageView.frame = CGRectMake(arc4random_uniform(CGRectGetWidth(self.view.bounds)), arc4random_uniform(CGRectGetHeight(self.view.bounds)), image.size.width * .25, image.size.height * .25); 

    CAShapeLayer *shape = [CAShapeLayer layer]; 
    shape.path = path; 
    imageView.layer.mask = shape; 

    [self.view addSubview:imageView]; 
    [imageView release]; 

} 
CGPathRelease(path); 

Mit dem obigen Code, imageView sehr laggy ist. Es ist jedoch reagiert sofort, wenn ich es manuell in einer bitmap context Maske:

UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"3.0-Pad-Classic0.jpg" ofType:nil]]; 
CGMutablePathRef path = CGPathCreateMutable(); 
CGPathAddEllipseInRect(path, NULL, CGRectMake(0.f, 0.f, image.size.width * .25, image.size.height * .25)); 


for (int i = 0; i < 200; i++) { 

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(image.size.width * .25, image.size.height * .25), NO, [[UIScreen mainScreen]scale]); 
    CGContextRef ctx = UIGraphicsGetCurrentContext(); 

    CGContextAddPath(ctx, path); 
    CGContextClip(ctx); 

    [image drawInRect:CGRectMake(-(image.size.width * .25), -(image.size.height * .25), image.size.width, image.size.height)]; 
    UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 

    SLTUIImageView *imageView = [[SLTUIImageView alloc]initWithImage:finalImage]; 
    imageView.frame = CGRectMake(arc4random_uniform(CGRectGetWidth(self.view.bounds)), arc4random_uniform(CGRectGetHeight(self.view.bounds)), finalImage.size.width, finalImage.size.height); 

    [self.view addSubview:imageView]; 
    [imageView release]; 

} 
CGPathRelease(path); 

By the way, hier ist der Code zu SLTUIImageView, es ist nur eine einfache Unterklasse von UIImageView, die (für jeden, der frage mich) auf Berührungen reagiert :

-(id)initWithImage:(UIImage *)image{ 

    self = [super initWithImage:image]; 
    if (self) { 
     self.userInteractionEnabled = YES; 
    } 
    return self; 
} 

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 
    [self.superview bringSubviewToFront:self]; 
} 

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ 

    UITouch *touch = [touches anyObject]; 
    self.center = [touch locationInView:self.superview]; 
} 

ist es möglich, irgendwie zu optimieren, wie die CAShapeLayer die UIImageView maskiert, so dass die Leistung verbessert wird? Ich habe versucht, herauszufinden, wo der Flaschenhals die Time Profiler in Instruments verwendet, aber ich kann nicht genau sagen, was es verursacht.

Ich habe versucht, shouldRasterize zu YES sowohl auf layer als auch auf layer.mask zu setzen, aber keiner scheint irgendeinen Effekt zu haben. Ich bin mir nicht sicher, was ich tun soll.

Edit:

Ich habe mehr Tests durchgeführt und finden, dass, wenn ich nur eine ganz normale CALayer verwenden Sie einen anderen CALayer (layer.mask = someOtherLayer) ich die gleichen Performance-Probleme haben zu maskieren. Es scheint, dass das Problem nicht spezifisch ist für CAShapeLayer -aber es ist spezifisch für die mask Eigenschaft von CALayer.

Edit 2:

So nach mehr über die Verwendung der Core Animation tool in Instruments lernen, habe ich gelernt, dass die Ansicht außerhalb des Bildschirms jedes Mal bewegt gerendert wird. Die Einstellung shouldRaster auf YES, wenn die Berührung anfängt und sie ausschaltet, wenn die Berührung endet, lässt die Ansicht in instruments grün bleiben (also den Cache behalten), aber die Leistung ist immer noch schrecklich. Ich glaube, das liegt daran, dass, obwohl die Ansicht zwischengespeichert wird, wenn sie nicht undurchsichtig ist, sie immer noch mit jedem Bild neu gerendert werden muss.

Eine Sache zu betonen ist, dass, wenn es nur ein paar Ansichten maskiert sind (sagen, sogar um zehn) die Leistung ist ziemlich gut. Wenn Sie das jedoch auf 100 or more erhöhen, bleibt die Leistung zurück. Ich stelle mir das vor, denn wenn man sich über die anderen bewegt, müssen sie alle neu gerendert werden.

Mein Fazit ist dies, Ich habe eine von zwei Möglichkeiten.

Zuerst muss es eine Möglichkeit geben, eine Ansicht dauerhaft zu maskieren (einmal rendern und gut nennen).Ich weiß, dass dies über die Grafik- oder Bitmapkontextroute geschehen kann, wie ich in meinem Beispielcode zeige, aber wenn eine Ebene ihre Ansicht maskiert, geschieht dies sofort. Wenn ich es in einem Bitmap-Kontext wie gezeigt mache, ist es ziemlich langsam (da es fast nicht einmal verglichen werden kann, wie viel langsamer es ist).

Zweitens muss es einige faster Möglichkeit geben, es über die Bitmap-Kontext-Route zu tun. Wenn es einen Experten gibt, Bilder oder Ansichten zu maskieren, würde ihre Hilfe sehr geschätzt werden.

+0

Ihre beste Wette ist in der Tat zu komprimieren Sie Ihre 200 Bilder, die Sie in Ihrer For-Schleife in 1 großes Bild mit etwas wie der Code hier erwähnt: http://stackoverflow.com/questions/4334233/how-to-capture- uiview-to-uiimage-ohne-verlust-von-qualität-auf-retina-display Sie könnten auch versuchen, Instrumente zu verwenden und den Zeitprofiler verwenden, um zu überprüfen, wo der Flaschenhals Ihres Codes ist. –

+0

Haben Sie versucht, die Bilder im Voraus in ihren endgültigen benötigten Zustand zu rendern, bevor Sie sie in der App verwenden, so dass die App nicht all diese Grafikverarbeitung durchführen muss, insbesondere da Sie so viele Bilder haben, um diese Verarbeitung durchzuführen Sie werden herumbewegt, so dass sie alle ständig neu rendern müssen. Ich bin mir nicht sicher, ob das eine brauchbare Option für Sie ist und ob Sie es schon versucht haben. Es ist auch schwierig zu erfassen, was Sie mit den Bildern ohne ein Bild zu tun versuchen, in diesem Fall ist es wichtig, wenn Sie einen Screenshot anhängen, würde es helfen. –

+1

Dies liegt an dem Offscreen-Rendering, auf das Sie bereits hingewiesen haben. Ich glaube nicht, dass es einen Weg gibt, um die Leistung der Ebene zu optimieren, aber wie Sie bereits gesehen haben, können Sie den Prozess optimieren. Ein weiterer Schritt könnte sein, dass die UIView-Klasse die DrawRect überschreibt, um das zu erreichen, was Sie brauchen. Der DrawRect wird nur aufgerufen, wenn Ihre Ansicht als schmutzig markiert ist. Keine Unterklasse UIImageView bacuse drawRect wird nie aufgerufen. – Andrea

Antwort

1

Sie sind ziemlich weit gekommen und ich glaube fast zu einer Lösung. Was ich tun würde, ist einfach eine Erweiterung dessen, was Sie bereits versucht haben. Da Sie sagen, dass viele dieser Ebenen in endgültigen Positionen "enden", die relativ zu den anderen Ebenen und der Maske konstant bleiben. Rendern Sie also einfach alle diese "fertigen" Ebenen in einen einzigen Bitmap-Kontext. Auf diese Weise haben Sie jedes Mal, wenn Sie eine Ebene in diesen einzelnen Kontext schreiben, eine Ebene weniger, um die Sie sich kümmern müssen, um den Animations-/Renderingprozess zu verlangsamen.

0

Quartz (drawRect :) ist aus vielen Gründen langsamer als CoreAnimation: CALayer vs CGContextdrawRect vs CALayer. Aber es ist notwendig, es richtig zu verwenden.

In der Dokumentation können Sie einige Ratschläge sehen. ImprovingCoreAnimationPerformance

Wenn Sie eine hohe Leistung wünschen, können Sie vielleicht versuchen, AsyncDisplayKit zu verwenden. Dieses Framework ermöglicht es, reibungslose und reaktionsschnelle Apps zu erstellen.