Ich arbeite durch die 4. ed. von dem Hillegass/Preble Cocoa Buch und habe einen Haken gefunden, den ich in Core Animation nicht verstehen kann.CATransaction Begin/Commit in Schleife nicht geehrt wird, geschieht alles auf einmal
Insbesondere habe ich die folgende Schleife, die eine Liste von Bildern, angeblich einzeln, in eine Schicht-Hosting-Ansicht präsentiert. Das erwartete Ergebnis ist, dass ich jedes Bild einzeln fliegen sehen sollte, wie presentImage
aufgerufen wird. Das tatsächliche Ergebnis ist, dass die Ansicht leer bleibt, bis die Schleife abgeschlossen ist und die Bilder dann alle gleichzeitig ausgeflogen werden. Während der Verzögerung sehe ich die mehreren Protokollnachrichten, die die mehreren Aufrufe an presentImage
anzeigen. Wenn diese Protokollmeldungen aufhören zu zeigen, dass die Schleife beendet ist, werden alle Bilder auf einmal ausgelöst.
// loop to present each image in list of URLs
// called at end of applicationDidFinishLaunching
NSTimeInterval t0 = [NSDate timeIntervalSinceReferenceDate];
for(id url in urls) {
NSImage *img = [[NSImage alloc] initWithContentsOfURL:url];
if(!img)
continue;
NSImage *thumbImg = [self thumbImageFromImage:img];
[self presentImage:thumbImg];
NSString *dt = [NSString stringWithFormat:@"%0.1fs",
[NSDate timeIntervalSinceReferenceDate]-t0];
NSLog(@"Presented %@ at %@ sec",url,dt);
}
Die thumbImageFromImage
Methode nur das tut, was man denkt (erzeugt ein kleineres Bild). Der Code für presentImage
ist wie folgt:
- (void)presentImage:(NSImage *)image
{
CGRect superlayerBounds = view.layer.bounds;
NSPoint center = NSMakePoint(CGRectGetMidX(superlayerBounds), CGRectGetMidY(superlayerBounds));
NSRect imageBounds = NSMakeRect(0, 0, image.size.width, image.size.height);
CGPoint randomPoint = CGPointMake(CGRectGetMaxX(superlayerBounds)*(double)random()/(double)RAND_MAX, CGRectGetMaxY(superlayerBounds)*(double)random()/(double)RAND_MAX);
CAMediaTimingFunction *tf = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
CABasicAnimation *posAnim = [CABasicAnimation animation];
posAnim.fromValue = [NSValue valueWithPoint:center];
posAnim.duration = 1.5;
posAnim.timingFunction = tf;
CABasicAnimation *bdsAnim = [CABasicAnimation animation];
bdsAnim.fromValue = [NSValue valueWithRect:NSZeroRect];
bdsAnim.duration = 1.5;
bdsAnim.timingFunction = tf;
CALayer *layer = [CALayer layer];
layer.contents = image;
layer.actions = [NSDictionary dictionaryWithObjectsAndKeys:posAnim,@"position", bdsAnim, @"bounds", nil];
[CATransaction begin];
[view.layer addSublayer:layer];
layer.position = randomPoint;
layer.bounds = NSRectToCGRect(imageBounds);
[CATransaction commit];
}
Das beobachtete Ergebnis ist, als ob die [CATransaction begin]
und [CATransaction commit]
Anrufe die for
Schleife anstatt den Anruf zu addSublayer
innerhalb presentImage
sind Belichtungsreihen. Tatsächlich beobachte ich das gleiche All-at-Once-Verhalten, wenn ich die [CATransaction begin]
und [CATransaction commit]
von innen presentImage
entferne und stattdessen die for
Schleife einreihe. Tatsächlich beobachte ich das gleiche All-at-Once-Verhalten, wenn ich Anrufe insgesamt zu [CATransaction begin]
und [CATransaction commit]
entferne.
Danke, Adam. Ich schätze es. Ich bin froh, dass das Verhalten zumindest von jemandem zu erwarten ist :-). Ich finde es ziemlich seltsam. Es scheint mir, dass jede Iteration der 'for (id url in urls)' - Schleife ein Bild laden und dann die Präsentation dieses Bildes animieren sollte, unabhängig davon, welcher Thread die Arbeit macht. Es ist mir weder aus dem Code noch aus der "CATransaction" -Dokumentation klar, dass die "CATransaction" -Aufrufe möglicherweise ignoriert werden. Ich werde diese Dokumentation jedoch bald sorgfältiger lesen. – JefferyRPrice
Ich würde nicht sagen, dass sie ignoriert werden, aber mehr, dass sie keine Chance bekommen, wirksam zu werden, da der Hauptthread keine Chance hat, mit anderen Ereignissen (wie der Animation) umzugehen, bis danach - addImagesFromFolderURL: gibt zurück. In Cocoa (und anderen Frameworks) sollten Sie Folgendes beachten: Ermöglichen, dass der Hauptthread fortgesetzt wird, sodass die Benutzeroberfläche aktualisiert und auf Benutzereingaben reagiert werden kann. –
Nochmals vielen Dank! Jetzt, wo ich das Concurrency-Kapitel beendet habe, muss ich sagen, dass ich immer noch verwirrt bin. Alle Bilder erscheinen immer noch auf einmal, wenn auch schneller (2.0s vs. 3.0s). Die Benutzeroberfläche wird alle gleichzeitig aktualisiert, als ob ich nur eine Animation festgeschrieben hätte. Mir scheint, dass ich mehrere Animationen gemacht habe, eine in jedem Durchgang durch die Schleife. Wie würde ich anfangen, die Bilder einzeln erscheinen zu lassen? (Ich habe versucht, 'addImagesFromFolderURL' aus' applicationDidFinishLaunch' zu entfernen und stattdessen das Ziel einer Schaltfläche zu machen. Immer noch gleiche Ergebnisse.) – JefferyRPrice