Chaining Core Animazioni animate

Qual è il modo più elegante e modulare di concatenare l’animazione in un contesto di Animazione di base ?

Intendo realizzare animazioni che iniziano proprio quando altre sono finite (ad esempio, cambiando la position e poi l’ opacity ). L’approccio normale è quello di modificare direttamente le proprietà:

 layer.position = new_point; layer.opacity = 0.0f; 

ma questo lo farà allo stesso tempo. Voglio far aspettare l’altro.

E che dire concatenare le animazioni per oggetti diversi? Ho letto su CATransaction usato come:

 [CATransaction begin] layer1.property = new_property; [CATransaction begin] layer2.property2 = new_property2; [CATransaction commit]; [CATransaction commit]; 

ma non sembra funzionare ..

Puoi anche utilizzare il raggruppamento di animazioni e utilizzare il campo beginTime dell’animazione. Prova qualcosa del genere:

 CABasicAnimation *posAnimation = [CABasicAnimation animationWithKeyPath:@"position"]; [posAnimation setFromValue:[NSNumber numberWithFloat:0.0]]; [posAnimation setToValue:[NSNumber numberWithFloat:1.0]]; // Here's the important part [posAnimation setDuration:10.0]; [posAnimation setBeginTime:0.0]; CABasicAnimation *borderWidthAnimation = [CABasicAnimation animationWithKeyPath:@"borderWidth"]; [borderWidthAnimation setFromValue:[NSNumber numberWithFloat:0.0]]; [borderWidthAnimation setToValue:[NSNumber numberWithFloat:1.0]]; // Here's the important part [borderWidthAnimation setDuration:10.0]; [borderWidthAnimation setBeginTime:5.0]; CAAnimationGroup *group = [CAAnimationGroup animation]; [group setDuration:10.0]; [group setAnimations:[NSArray arrayWithObjects:posAnimation, borderWidthAnimation, nil]]; [layer addAnimation:group forKey:nil]; 

Si noti che la durata dell’intera animazione è di 10 secondi. Il primo inizia al secondo 0 e il secondo inizia a 5 secondi.

Come Matt ha sottolineato, è ansible creare gruppi di animazione che consistono in diverse animazioni per lo stesso livello con tempi di inizio diversi. È inoltre ansible impostare un delegato per CAAnimation oggetti CAAnimation autonomi o CAAnimation Groups e, al termine di ciascuna animazione, chiamerà un metodo animationDidStop:finished: delegate (si noti che le animazioni che fanno parte di un gruppo non chiameranno l’ animationDidStop:finished: del proprio delegatoDidStop animationDidStop:finished: metodo.

Ho scoperto un trucco interessante che rende più potente l’ CAAnimation animationDidStop:finished: metodo. Uso il metodo setValue:forKey: per aggiungere un blocco di codice a un’animazione autonoma o un gruppo di animazione, con la chiave @ “animationCompletionBlock”. Quindi scrivo animationDidStop:finished: generaleDidStop animationDidStop:finished: metodo che controlla l’animazione appena completata per una chiave @ “animationCompletionBlock” e, se la trova, esegue il blocco di codice lì.

Dai un’occhiata a questo progetto su github per un esempio funzionante di questa tecnica:

Demo CAAnimation con blocchi di completamento

È anche impostato un gruppo di animazioni all’interno di a

 [CATransaction begin]; //... [[CATransaction commit]; 

blocco, come hai suggerito. Quando lo fai, puoi utilizzare il metodo della class CATransaction setCompletionBlock: per richiamare un blocco di codice quando tutte le animazioni nel gruppo di transazioni corrente sono complete. Il blocco di completamento per una transazione può quindi triggersre la transazione successiva.

Non credo che tu possa “annidare” le animazioni CA come nel tuo esempio.

È necessario specificare un delegato per l’animazione e inserire la seconda “transizione” all’interno animationDidStop:finished: selettore del delegato.

Potrebbe volere dare un’occhiata ai tipi di animazione e alla programmazione temporale di Apple.

Quello che ho sempre preferito impostare l’ora di inizio e fine di ogni animazione separatamente è questo:

Ho usato A2DynamicDelegate (il cui sviluppo sta ora accadendo in BlocksKit -Repo, chissà perché <_ <) per implementare una proprietà completionBlock in una categoria su CAAnimation.

Questo mi ha permesso di fare cose del genere:

 CAAnimation *a = ... CAAnimation *b = ... CAAnimation *c = ... a.completionHandler = ^{ [self.layer addAnimation:b forKey:@"foo"]; [self.layer addAnimation:c forKey:@"bar"]; }; 

Molto più flessibile 🙂

Ho caricato il mio codice per il gestore di completamento qui . Dai un’occhiata però all’avviso nel file di intestazione. Sono davvero confuso perché il metodo non viene chiamato.

setCompletionBlock questo utilizzando il metodo setCompletionBlock per definire una chiusura che triggers l’animazione successiva al termine della prima:

 [CATransaction begin] layer1.property = new_property; CATransaction.setCompletionBlock { [CATransaction begin] layer2.property2 = new_property2; [CATransaction commit]; } [CATransaction commit]; 

Senza includere tutti i “trucchi” nella mia “toolchain”, questo esempio non è direttamente copiabile / passibile … ma mostra una strategia DAVVERO facile per le animazioni “concatenate”.

 CATransform3D trans = m34(); // define chain-wide constants. // Name your "stack". My "nextObject" returns arr[i]->done == nil. NSArray *layerStack = layer.sublayers; //define a block, that "takes" a layer as it's argument. void(^__block ChainBlock)(CALayer*) = ^(CALayer *m) { // animations, transforms, etc for each inividual "step". [m animate:@"transform" // These are just NSValue-wrapped CAT3D's from:AZV3d(CATransform3DRotate(trans, 0,1,0,0)) to:AZV3d(CATransform3DRotate(trans,1.5,1,0,0)) time:2 // total time == each step * layerStack.count eased:kCAMediaTimingFunctionEaseOut completion:^{ // In completion, look for "next" layer. CAL* m2 = [layers nextObject]; // If there is "another" layer, call this block, again... with it. if (m2) chainAnis(m2); // Otherise,you're done. Cleanup, toggle values, whatevs. else self.finishedProperty = YES; }]; }; // Give the block we just defined your "first" layer. ChainBlock(layerStack[0]); // It will recursively feed itself. 

Questo ovviamente dipende da qualche “magia esterna”, ma il concetto è semplice ed elimina (attraverso le dipendenze) la necessità di “trattare” QUALSIASI tipo di delega grossolana. In particolare, le categorie animate:from:to:time:easing:completion , etc. provengono dal grande FunSize Framework, su Github .