Esecuzione di animazioni iOS su viste con UIKit e UIView

Pubblicato: 2022-03-10
Riassunto rapido ↬ Questo articolo vuole essere un'introduzione alle animazioni iOS che coprono in modo esauriente i diversi modi per farlo. Iniziamo con la comprensione delle basi delle animazioni, passiamo ai Core Framework costruendo un singolo esempio utilizzando i diversi metodi offerti e infine cercando modi per ottimizzare le prestazioni.

Sono uno sviluppatore iOS da oltre un decennio e raramente ho visto articoli che consolidano tutti i modi possibili per eseguire animazioni in iOS. Questo articolo vuole essere un'introduzione alle animazioni iOS con l'intento di coprire in modo esauriente i diversi modi per fare lo stesso.

Data l'ampiezza dell'argomento, tratteremmo succintamente ogni parte ad un livello abbastanza alto. L'obiettivo è educare il lettore con una serie di scelte per aggiungere animazioni alla sua app iOS.

Prima di iniziare con argomenti relativi a iOS, diamo una breve occhiata alla velocità di animazione.

Animazione a 60 FPS

Generalmente nei video, ogni fotogramma è rappresentato da un'immagine e la frequenza fotogrammi determina il numero di immagini capovolte nella sequenza. Questo è definito come "frame al secondo" o FPS.

FPS determina il numero di immagini fisse capovolte in un secondo, il che significa letteralmente che maggiore è il numero di immagini/fotogrammi, più dettagli/informazioni vengono visualizzati nel video. Questo vale anche per le animazioni.

L'FPS viene in genere utilizzato per determinare la qualità delle animazioni. C'è un'opinione popolare secondo cui qualsiasi buona animazione dovrebbe funzionare a 60 fps o superiore: qualsiasi cosa inferiore a 60 fps sembrerebbe un po' fuori luogo.

Vuoi vedere la differenza tra 30FPS e 60FPS? Controllare questo!

Hai notato la differenza? Gli occhi umani possono sicuramente sentire il jitter a fps più bassi. Pertanto, è sempre una buona pratica assicurarsi che qualsiasi animazione creata aderisca alla regola di base dell'esecuzione a 60 FPS o superiore. Questo lo fa sentire più realistico e vivo.

Dopo aver esaminato gli FPS, analizziamo ora i diversi framework iOS di base che ci forniscono un modo per eseguire animazioni.

Altro dopo il salto! Continua a leggere sotto ↓

Quadri di base

In questa sezione, analizzeremo i framework nell'SDK iOS che possono essere utilizzati per creare animazioni di visualizzazione. Faremo una breve passeggiata attraverso ciascuno di essi, spiegando il loro set di funzionalità con un esempio pertinente.

Animazioni UIKit/ UIView

UIView è la classe base per qualsiasi visualizzazione che mostra contenuto nelle app iOS.

UIKit, il framework che ci offre UIView, ci fornisce già alcune funzioni di animazione di base che rendono conveniente per gli sviluppatori ottenere di più facendo meno.

L'API, UIView.animate , è il modo più semplice per animare le viste poiché le proprietà di qualsiasi vista possono essere facilmente animate fornendo i valori delle proprietà nella sintassi basata sui blocchi.

Nelle animazioni UIKit, si consiglia di modificare solo le proprietà animabili di UIVIew, altrimenti ci saranno ripercussioni in cui le animazioni potrebbero far finire la vista in uno stato imprevisto.

animazione (conDurata: animazioni: completamento)

Questo metodo accetta la durata dell'animazione, un insieme di modifiche alle proprietà animabili della vista che devono essere animate. Il blocco di completamento fornisce una richiamata quando la visualizzazione è terminata con l'esecuzione dell'animazione.

Quasi ogni tipo di animazione come lo spostamento, il ridimensionamento, la rotazione, la dissolvenza e così via su una vista può essere ottenuto con questa singola API.

Ora, considera che vuoi animare una modifica alla dimensione del pulsante o vuoi che una vista particolare ingrandisca lo schermo. Ecco come possiamo farlo usando l'API UIView.animate :

 let newButtonWidth: CGFloat = 60 UIView.animate(withDuration: 2.0) { //1 self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth) //2 self.button.center = self.view.center //3 }

Ecco cosa stiamo facendo qui:

  1. Chiamiamo il metodo UIView.animate con un valore di durata passato che rappresenta per quanto tempo deve essere eseguita l'animazione, descritta all'interno del blocco.
  2. Impostiamo il nuovo frame del pulsante che dovrebbe rappresentare lo stato finale dell'animazione.
  3. Impostiamo il centro del pulsante con il center della sua superview in modo che rimanga al centro dello schermo.

Il blocco di codice di animazione sopra dovrebbe attivare l'animazione del fotogramma del pulsante che cambia dal fotogramma corrente:

Width = 0, Height = 0

Al fotogramma finale:

Width = Height = newButtonWidth

Ed ecco come sarebbe l'animazione:

animateWithDuration

Questo metodo è come un'estensione del metodo animato in cui puoi fare tutto ciò che puoi eseguire nell'API precedente con alcuni comportamenti fisici aggiunti alle animazioni della vista.

Ad esempio, se vuoi ottenere effetti di smorzamento primaverile nell'animazione che abbiamo fatto sopra, ecco come sarebbe il codice:

 let newButtonWidth: CGFloat = 60 UIView.animate(withDuration: 1.0, //1 delay: 0.0, //2 usingSpringWithDamping: 0.3, //3 initialSpringVelocity: 1, //4 options: UIView.AnimationOptions.curveEaseInOut, //5 animations: ({ //6 self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth) self.button.center = self.view.center }), completion: nil)

Ecco l'insieme di parametri che utilizziamo:

  1. duration
    Rappresenta la durata dell'animazione che determina per quanto tempo deve essere eseguito il blocco di codice.
  2. delay
    Rappresenta il ritardo iniziale che vogliamo avere prima dell'inizio dell'animazione.
  3. SpringWithDamping
    Rappresenta il valore dell'effetto elastico che vogliamo che la vista si comporti. Il valore deve essere compreso tra 0 e 1. Più basso è il valore, maggiore è l'oscillazione della molla.
  4. velocity
    Rappresenta la velocità di avvio dell'animazione.
  5. options
    Tipo di curva di animazione che desideri applicare all'animazione della vista.
  6. Infine, il blocco di codice dove impostiamo la cornice del pulsante che deve essere animato. È lo stesso dell'animazione precedente.

Ed ecco come sarebbe l'animazione con la configurazione dell'animazione sopra:

UIViewPropertyAnimator

Per un po' più di controllo sulle animazioni, UIViewPropertyAnimator è utile dove ci fornisce un modo per mettere in pausa e riprendere le animazioni. Puoi avere tempi personalizzati e rendere la tua animazione interattiva e interrompibile. Questo è molto utile quando si eseguono animazioni che sono anche interagibili con le azioni dell'utente.

Il classico gesto "Scorri per sbloccare" e l'animazione di chiusura/espansione della vista del lettore (nell'app Musica) sono esempi di animazioni interattive e interrompibili. Puoi iniziare a spostare una vista con il dito, quindi rilasciarla e la vista tornerà alla sua posizione originale. In alternativa, puoi catturare la vista durante l'animazione e continuare a trascinarla con il dito.

Di seguito è riportato un semplice esempio di come potremmo ottenere l'animazione utilizzando UIViewPropertyAnimator :

 let newButtonWidth: CGFloat = 60 let animator = UIViewPropertyAnimator(duration:0.3, curve: .linear) { //1 self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth) self.button.center = self.view.center } animator.startAnimation() //2

Ecco cosa stiamo facendo:

  1. Chiamiamo l'API UIViewProperty passando la durata e la curva di animazione.
  2. A differenza di entrambe le API UIView.animate di cui sopra, l'animazione non si avvia a meno che non la specifichi tu stesso, ovvero hai il pieno controllo dell'intero processo/flusso di animazione.

Ora, supponiamo che tu voglia ancora più controllo sulle animazioni. Ad esempio, vuoi progettare e controllare ogni singolo fotogramma nell'animazione. C'è un'altra API per questo, animateKeyframes . Ma prima di approfondire, diamo un'occhiata rapidamente a cos'è un frame, in un'animazione.

Che cos'è una frame ?

Una raccolta di modifiche/transizioni di frame della vista, dallo stato iniziale allo stato finale, è definita come animation e ogni posizione della vista durante l'animazione è chiamata frame .

animare i fotogrammi chiave

Questa API fornisce un modo per progettare l'animazione in modo tale da poter definire più animazioni con tempi e transizioni diversi. Pubblica questo, l'API integra semplicemente tutte le animazioni in un'unica esperienza senza interruzioni.

Diciamo che vogliamo spostare il nostro pulsante sullo schermo in modo casuale. Vediamo come possiamo utilizzare l'API di animazione dei fotogrammi chiave per farlo.

 UIView.animateKeyframes(withDuration: 5, //1 delay: 0, //2 options: .calculationModeLinear, //3 animations: { //4 UIView.addKeyframe( //5 withRelativeStartTime: 0.25, //6 relativeDuration: 0.25) { //7 self.button.center = CGPoint(x: self.view.bounds.midX, y: self.view.bounds.maxY) //8 } UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.25) { self.button.center = CGPoint(x: self.view.bounds.width, y: start.y) } UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 0.25) { self.button.center = start } })

Ecco la ripartizione:

  1. duration
    Chiama l'API passando la durata dell'animazione.
  2. delay
    Durata iniziale del ritardo dell'animazione.
  3. options
    Il tipo di curva di animazione che desideri applicare all'animazione della vista.
  4. animations
    Blocco che accetta tutte le animazioni dei fotogrammi chiave progettate dallo sviluppatore/utente.
  5. addKeyFrame
    Chiama l'API per progettare ogni singola animazione. Nel nostro caso, abbiamo definito ogni mossa del pulsante. Possiamo avere tutte le animazioni di cui abbiamo bisogno, aggiunte al blocco.
  6. relativeStartTime
    Definisce l'ora di inizio dell'animazione nella raccolta del blocco di animazione.
  7. relativeDuration
    Definisce la durata complessiva di questa specifica animazione.
  8. center
    Nel nostro caso, cambiamo semplicemente la proprietà center del pulsante per spostare il pulsante sullo schermo.

Ed ecco come appaiono le animazioni finali:

CoreAnimation

Qualsiasi animazione basata su UIKit viene tradotta internamente in animazioni di base. Pertanto, il framework Core Animation funge da livello di supporto o spina dorsale per qualsiasi animazione UIKit. Pertanto, tutte le API di animazione UIKit non sono altro che livelli incapsulati delle API di animazione principali in modo facilmente consumabile o conveniente.

Le API di animazione UIKit non forniscono molto controllo sulle animazioni che sono state eseguite su una vista poiché vengono utilizzate principalmente per le proprietà animabili della vista. Pertanto, in questi casi, in cui si intende avere il controllo su ogni fotogramma dell'animazione, è meglio utilizzare direttamente le API di animazione di base sottostanti. In alternativa, sia le animazioni UIView che le animazioni principali possono essere utilizzate insieme.

UIView + Animazione principale

Vediamo come possiamo ricreare la stessa animazione di modifica del pulsante insieme a specificare la curva di temporizzazione utilizzando le API UIView e Core Animation.

Possiamo utilizzare le funzioni di temporizzazione di CATransaction , che consentono di specificare e controllare la curva di animazione.

Diamo un'occhiata a un esempio di un'animazione di modifica della dimensione del pulsante con il raggio dell'angolo che utilizza la funzione di temporizzazione di CATransaction e una combinazione di animazioni UIView:

 let oldValue = button.frame.width/2 let newButtonWidth: CGFloat = 60 /* Do Animations */ CATransaction.begin() //1 CATransaction.setAnimationDuration(2.0) //2 CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)) //3 // View animations //4 UIView.animate(withDuration: 1.0) { self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth) self.button.center = self.view.center } // Layer animations let cornerAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.cornerRadius)) //5 cornerAnimation.fromValue = oldValue //6 cornerAnimation.toValue = newButtonWidth/2 //7 button.layer.cornerRadius = newButtonWidth/2 //8 button.layer.add(cornerAnimation, forKey: #keyPath(CALayer.cornerRadius)) //9 CATransaction.commit() //10

Ecco la ripartizione:

  1. begin
    Rappresenta l'inizio del blocco di codice dell'animazione.
  2. duration
    Durata complessiva dell'animazione.
  3. curve
    Rappresenta la curva di temporizzazione da applicare all'animazione.
  4. UIView.animate
    La nostra prima animazione per cambiare la cornice del pulsante.
  5. CABasicAnimation
    Creiamo l'oggetto CABasicAnimation facendo riferimento al cornerRadius del pulsante come percorso chiave poiché è quello che vogliamo animare. Allo stesso modo, se vuoi avere un controllo di livello granulare sulle animazioni dei fotogrammi chiave, puoi usare la classe CAKeyframeAnimation .
  6. fromValue
    Rappresenta il valore iniziale dell'animazione, ovvero il valore iniziale cornerRadius del pulsante da cui deve iniziare l'animazione.
  7. toValue
    Rappresenta il valore finale dell'animazione, ovvero il valore cornerRadius finale del pulsante dove deve terminare l'animazione.
  8. cornerRadius
    Dobbiamo impostare la proprietà cornerRadius del pulsante con il valore finale dell'animazione, altrimenti il ​​valore cornerRadius del pulsante verrà riportato automaticamente al valore iniziale al termine dell'animazione.
  9. addAnimation
    Alleghiamo l'oggetto di animazione che contiene la configurazione dell'intero processo di animazione al livello rappresentando il Keypath per il quale deve essere eseguita l'animazione.
  10. commit
    Rappresenta la fine del blocco di codice dell'animazione e avvia l'animazione.

Ecco come sarebbe l'animazione finale:

Questo blog è un'ottima lettura per aiutarti a creare animazioni più avanzate poiché ti guida in modo ordinato attraverso la maggior parte delle API del framework Core Animation con istruzioni che ti guidano attraverso ogni fase del processo.

UIKitDynamics

UIKit Dynamics è il motore fisico per UIKit che ti consente di aggiungere qualsiasi comportamento fisico come collisione, gravità, spinta, snap, ecc. ai controlli UIKit.

UIKitDynamicAnimator

Questa è la classe admin del framework UIKit Dynamics che regola tutte le animazioni attivate da un determinato controllo dell'interfaccia utente.

UIKitDynamicBehavior

Ti consente di aggiungere qualsiasi comportamento fisico a un animatore che quindi gli consente di eseguire sulla vista ad esso collegata.

Diversi tipi di comportamenti per UIKitDynamics includono:

  • UIAttachmentBehavior
  • UICollisionBehavior
  • UIFieldBehavior
  • UIGravityBehavior
  • UIPushBehavior
  • UISnapBehavior

L'architettura di UIKitDynamics è simile a questa. Si noti che gli elementi da 1 a 5 possono essere sostituiti con una vista singola.

Applichiamo alcuni comportamenti fisici al nostro pulsante. Vedremo come applicare la gravità al pulsante in modo che ci dia la sensazione di avere a che fare con un oggetto reale.

 var dynamicAnimator : UIDynamicAnimator! var gravityBehavior : UIGravityBehavior! dynamicAnimator = UIDynamicAnimator(referenceView: self.view) //1 gravityBehavior = UIGravityBehavior(items: [button]) //2 dynamicAnimator.addBehavior(gravityBehavior) //3

Ecco la ripartizione:

  1. UIKitDynamicAnimator
    Abbiamo creato un oggetto UIKitDynamicAnimator che funge da orchestratore per l'esecuzione di animazioni. Abbiamo anche passato la superview del nostro pulsante come vista di riferimento.
  2. UIGravityBehavior
    Abbiamo creato un oggetto UIGravityBehavior e passato il nostro pulsante negli elementi dell'array su cui viene iniettato questo comportamento.
  3. addBehavior
    Abbiamo aggiunto l'oggetto gravità all'animatore.

    Questo dovrebbe creare un'animazione come mostrato di seguito:
    Nota come il pulsante cade dal centro (la sua posizione originale) dello schermo verso il basso e oltre.
    Dovremmo dire all'animatore di considerare la parte inferiore dello schermo come terra. È qui che entra in UICollisionBehavior .

     var dynamicAnimator : UIDynamicAnimator! var gravityBehavior : UIGravityBehavior! var collisionBehavior : UICollisionBehavior! dynamicAnimator = UIDynamicAnimator(referenceView: self.view) //1 gravityBehavior = UIGravityBehavior(items: [button]) //2 dynamicAnimator.addBehavior(gravityBehavior) //3 collisionBehavior = UICollisionBehavior(items: [button]) //4 collisionBehavior.translatesReferenceBoundsIntoBoundary = true //5 dynamicAnimator.addBehavior(collisionBehavior) //6
  4. UICollisionBehavior
    Abbiamo creato un oggetto UICollisionBehavior e passato il pulsante in modo che il comportamento venga aggiunto all'elemento.
  5. translatesReferenceBoundsIntoBoundary
    L'abilitazione di questa proprietà indica all'animatore di considerare il confine delle viste di riferimento come fine, che nel nostro caso è la parte inferiore dello schermo.
  6. addBehavior
    Abbiamo aggiunto il comportamento di collisione all'animatore qui.

    Ora, il nostro pulsante dovrebbe toccare terra e rimanere fermo come mostrato di seguito:

    È abbastanza carino, vero?

    Ora, proviamo ad aggiungere un effetto di rimbalzo in modo che il nostro oggetto sembri più reale. Per fare ciò, utilizzeremo la classe UIDynamicItemBehavior .
     var dynamicAnimator : UIDynamicAnimator! var gravityBehavior : UIGravityBehavior! var collisionBehavior : UICollisionBehavior! var bouncingBehavior : UIDynamicItemBehavior! dynamicAnimator = UIDynamicAnimator(referenceView: self.view) //1 gravityBehavior = UIGravityBehavior(items: [button]) //2 dynamicAnimator.addBehavior(gravityBehavior) //3 collisionBehavior = UICollisionBehavior(items: [button]) //4 collisionBehavior.translatesReferenceBoundsIntoBoundary = true //5 dynamicAnimator.addBehavior(collisionBehavior) //6 //Adding the bounce effect bouncingBehavior = UIDynamicItemBehavior(items: [button]) //7 bouncingBehavior.elasticity = 0.75 //8 dynamicAnimator.addBehavior(bouncingBehavior) //9
  7. UIDynamicItemBehavior
    Abbiamo creato un oggetto UIDynamicItemBehavior e passato il pulsante in modo che il comportamento venga aggiunto all'elemento.
  8. elasticity
    Il valore deve essere compreso tra 0-1, rappresenta l'elasticità ovvero il numero di volte in cui l'oggetto deve rimbalzare su e giù dal suolo quando viene colpito. È qui che avviene la magia: modificando questa proprietà, puoi distinguere tra diversi tipi di oggetti come palline, bottiglie, oggetti rigidi e così via.
  9. addBehavior
    Abbiamo aggiunto il comportamento di collisione all'animatore qui.

Ora, il nostro pulsante dovrebbe rimbalzare quando colpisce il suolo, come mostrato di seguito:

Questo repository è abbastanza utile e mostra tutti i comportamenti di UIKitDynamics in azione. Fornisce anche codice sorgente per giocare con ogni comportamento. Questo, secondo me, dovrebbe servire come un ampio elenco di modi per eseguire animazioni iOS sulle visualizzazioni!

Nella prossima sezione daremo una breve occhiata agli strumenti che ci aiuteranno a misurare le prestazioni delle animazioni. Ti consiglierei anche di cercare modi per ottimizzare la tua build Xcode poiché farà risparmiare un'enorme quantità di tempo di sviluppo.

Ottimizzazione delle prestazioni

In questa sezione, esamineremo i modi per misurare e ottimizzare le prestazioni delle animazioni iOS. Come sviluppatore iOS, potresti aver già utilizzato strumenti Xcode come perdite di memoria e allocazioni per misurare le prestazioni dell'app complessiva. Allo stesso modo, ci sono strumenti che possono essere utilizzati per misurare le prestazioni delle animazioni.

Strumento Core Animation

Prova lo strumento Core Animation e dovresti essere in grado di vedere gli FPS forniti dalla schermata dell'app. Questo è un ottimo modo per misurare le prestazioni/la velocità di qualsiasi animazione renderizzata nella tua app iOS.

Disegno

L'FPS è notevolmente ridotto nell'app che mostra contenuti pesanti come immagini con effetti come le ombre. In questi casi, invece di assegnare l'immagine direttamente alla proprietà dell'immagine di UIImageView , prova a disegnare l'immagine separatamente in un contesto usando le API Core Graphics. Ciò riduce eccessivamente il tempo di visualizzazione dell'immagine eseguendo la logica di decompressione dell'immagine in modo asincrono quando viene eseguita in un thread separato anziché nel thread principale.

Rasterizzazione

La rasterizzazione è un processo utilizzato per memorizzare nella cache informazioni di livelli complessi in modo che queste viste non vengano ridisegnate ogni volta che vengono renderizzate. Il ridisegno delle viste è la causa principale della riduzione degli FPS e quindi è meglio applicare la rasterizzazione alle viste che verranno riutilizzate più volte.

Avvolgendo

Per concludere, ho anche riassunto un elenco di risorse utili per le animazioni iOS. Potresti trovarlo molto utile quando lavori su animazioni iOS. Inoltre, potresti anche trovare utile questo set di strumenti di progettazione come passaggio (di progettazione) prima di approfondire le animazioni.

Spero di essere stato in grado di trattare il maggior numero possibile di argomenti relativi alle animazioni di iOS. Se c'è qualcosa che potrei essermi perso in questo articolo, per favore fatemelo sapere nella sezione commenti qui sotto e sarei felice di fare l'aggiunta!