Transition (animazione di transizione) è un meccanismo di cambiamento visivo che sostituisce un contenuto con un altro: schermate, frammenti, viste o stati dell'interfaccia. Una transizione correttamente implementata dirige l'attenzione dell'utente, spiega la relazione tra gli elementi e rende la navigazione intuitiva. Secondo le Material Design Guidelines (2026), le app con transizioni ben progettate mostrano una fidelizzazione degli utenti del 22% superiore nella seconda sessione. Scopri di più sugli altri tipi di animazione nella guida generale all'animazione.
Punti chiave
Transition è un'animazione che si verifica quando si sostituisce uno stato dell'interfaccia visiva con un altro. A differenza dell'Property Animation (animazione di una proprietà di un singolo oggetto), Transition gestisce un intero insieme di cambiamenti: apparizione/scomparsa di elementi, modifica della loro posizione, dimensione e proprietà all'interno di un'unica transizione. Transition risolve il problema del cambio fluido di contenuto in modo che l'utente capisca cosa si è spostato e dove.
Sulle piattaforme mobili, Transition viene utilizzata in tre scenari principali: navigazione tra schermate (push, pop, modal present), cambio di frammenti (FragmentTransaction di Android) e cambio di stato all'interno della schermata (espansione di schede, cambio di tab). Ogni scenario richiede il proprio approccio: transizioni di navigazione — UIViewControllerAnimatedTransitioning in iOS o ActivityOptions in Android, transizioni intra-schermata — Transition Framework o UIView.transition.
Secondo l'Apple HIG (2026), la durata di una transizione standard non deve superare 400–500 ms — le animazioni più lunghe vengono percepite come ritardo. Material Design raccomanda 300–350 ms per le transizioni mobili. Per gli elementi grandi (schede, immagini), il tempo può aumentare fino a 500–600 ms senza perdita di prestazioni percepite.
I tipi di transizione si dividono in diverse categorie a seconda di quale contenuto cambia e come. Tipi base: cross-fade (dissolvenza) — scomparsa fluida di uno e apparizione di un altro; slide (scorrimento) — una schermata esce, un'altra entra; scale/zoom (ridimensionamento) — un elemento si ingrandisce, aprendo una schermata di dettaglio. Le transizioni combinate mescolano più effetti.
| Tipo di transizione | Descrizione | API iOS | API Android |
|---|---|---|---|
| Cross-fade | Dissolvenza: un contenuto scompare, un altro appare | UIView.transition(with:duration:options:.transitionCrossDissolve) | TransitionSet + Fade() |
| Slide | Scorrimento: il contenuto entra/esce orizzontalmente o verticalmente | CATransition(type: .push, subtype: .fromLeft) | Slide() / FragmentTransaction.setCustomAnimations |
| Scale / Zoom | Ridimensionamento: l'elemento si ingrandisce a schermo intero | UIViewPropertyAnimator con animazione di scala | Scale() + ActivityOptions.makeScaleUpAnimation |
| Explode | Esplosione: il contenuto si frammenta scomparendo | CATransition(type: .reveal) | Explode() (API 21+) |
| Fade-through | Dissolvenza attraverso schermo bianco/nero | UIView.transition(with: .transitionCrossDissolve, con sfondo bianco) | Fade() tramite ChangeBounds |
Regole Material Design: utilizzate un tipo di transizione all'interno di una singola app per creare un linguaggio visivo unificato. Raccomandato: slide per la navigazione tra schermate dello stesso livello (master → dettaglio), cross-fade per il cambio di contenuto all'interno di una schermata (tab, cambio vista), scale/zoom per la transizione da una scheda a una schermata di dettaglio (scheda prodotto → descrizione).
In iOS, UIView.transition è il modo più semplice per animare i cambiamenti di contenuto all'interno di una singola View. Supporta cross-dissolve, curl-up, curl-down, flip-from-left, flip-from-right. Per le transizioni di navigazione tra UIViewController, utilizzate UINavigationControllerDelegate con UIViewControllerAnimatedTransitioning — questo dà il controllo completo sull'animazione push/pop.
import UIKit
// UIView.transition: cambio immagine con cross-dissolve
UIView.transition(with: imageView,
duration: 0.4,
options: [.transitionCrossDissolve, .curveEaseInOut]
) {
self.imageView.image = UIImage(named: "new_image")
} completion: { _ in
print("Transition completed")
}
// UIView.transition: scambio di due viste
UIView.transition(from: firstView, to: secondView,
duration: 0.5,
options: [.transitionFlipFromRight, .showHideTransitionViews]
) { _ in
// firstView nascosta, secondView mostrata
}
// CATransition per layer (senza Auto Layout)
let transition = CATransition()
transition.type = .push
transition.subtype = .fromRight
transition.duration = 0.35
transition.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
view.layer.add(transition, forKey: "pushTransition")
UIViewPropertyAnimator è un'alternativa moderna per le transizioni personalizzate su iOS 10+. A differenza di UIView.transition, PropertyAnimator supporta pausa, annullamento e animazione inversa, importante per le transizioni interattive (scorrimento per tornare indietro). Per l'animazione tra UIViewController, utilizzate UIPercentDrivenInteractiveTransition insieme a PropertyAnimator — questo è il modello standard per la navigazione interattiva.
In Android, FragmentTransaction con setCustomAnimations è il modo principale per animare le transizioni tra Fragment. Parametri: enter (animazione di ingresso del nuovo Fragment), exit (animazione di uscita del vecchio Fragment), popEnter e popExit — per la navigazione indietro. FragmentTransaction supporta risorse di animazione (R.anim) e Transition Framework (R.transition).
import androidx.fragment.app.FragmentTransaction
import androidx.transition.TransitionInflater
// FragmentTransaction con animazioni di risorse
supportFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.slide_in_right, // enter
R.anim.slide_out_left, // exit
R.anim.slide_in_left, // popEnter
R.anim.slide_out_right // popExit
)
.replace(R.id.container, DetailFragment())
.addToBackStack(null)
.commit()
// FragmentTransaction con Transition Framework
val detailFragment = DetailFragment()
detailFragment.enterTransition = TransitionInflater.from(this)
.inflateTransition(R.transition.slide_right)
detailFragment.exitTransition = TransitionInflater.from(this)
.inflateTransition(R.transition.fade_out)
supportFragmentManager.beginTransaction()
.replace(R.id.container, detailFragment)
.addToBackStack(null)
.commit()
// ActivityOptions: animazione all'avvio di un'Activity
val options = ActivityOptions.makeScaleUpAnimation(
view, 0, 0, view.width, view.height
).toBundle()
startActivity(intent, options)
Buone pratiche Android: Slide() — per la sostituzione di Fragment nella navigazione (enter = Slide(Gravity.END), exit = Slide(Gravity.START)). Fade() — per mostrare/nascondere overlay e dialoghi. Explode() — per transizioni di accento (eliminare un elemento da un elenco). Per i dispositivi più vecchi (API < 21) utilizzate le risorse R.anim come fallback. Transition Framework non supporta SurfaceView, TextureView, GLSurfaceView — per video e fotocamera utilizzate solo animazioni R.anim.
UIViewControllerAnimatedTransitioning è un protocollo iOS per creare animazioni di transizione completamente personalizzate tra controller. Fornisce accesso alla vista contenitore (transitionContext.containerView), alla vista sorgente e alla vista destinazione. Combinato con UIPercentDrivenInteractiveTransition, questo approccio consente di creare transizioni interattive con scorrimento tattile.
import UIKit
"> Animatore di transizione personalizzato
class ScaleTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {
let isPresenting: Bool
init(presenting: Bool) { self.isPresenting = presenting }
func transitionDuration(using ctx: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.4
}
func animateTransition(using ctx: UIViewControllerContextTransitioning) {
guard let toView = ctx.view(forKey: .to),
let fromView = ctx.view(forKey: .from)
else { return }
let container = ctx.containerView
let initialScale: CGAffineTransform = CGAffineTransform(scaleX: 0.8, y: 0.8)
let finalFrame = ctx.finalFrame(for: toView)
if isPresenting {
toView.transform = initialScale
toView.alpha = 0
toView.frame = finalFrame
container.addSubview(toView)
}
UIView.animate(withDuration: transitionDuration(using: ctx),
delay: 0,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0.3) {
if self.isPresenting {
toView.transform = .identity
toView.alpha = 1
fromView.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
fromView.alpha = 0
} else {
fromView.transform = initialScale
fromView.alpha = 0
}
} completion: { _ in
fromView.transform = .identity
ctx.completeTransition(!ctx.transitionWasCancelled)
}
}
}
// Applicazione in UINavigationControllerDelegate
extension MainViewController: UINavigationControllerDelegate {
func navigationController(
_ nc: UINavigationController,
animationControllerFor operation: UINavigationController.Operation,
from: UIViewController, to: UIViewController
) -> UIViewControllerAnimatedTransitioning? {
return ScaleTransitionAnimator(presenting: operation == .push)
}
}
Transizioni interattive: aggiungete un UIPanGestureRecognizer alla schermata sorgente. Quando il gesto viene attivato, create un UIPercentDrivenInteractiveTransition e chiamate update(CGFloat(percent)). Per annullare quando la soglia (di solito 30%) non viene raggiunta, chiamate cancel(). Questo modello viene utilizzato per impostazione predefinita in UINavigationController per i gesti pop e in UIScrollView con pagingEnabled. Per i gesti personalizzati, implementate UIViewControllerInteractiveTransitioning.
Transition Framework (android.transition) è un sistema di animazione dichiarativo che anima automaticamente i cambiamenti tra due scene (Scene). Una scena è un insieme di Views con uno stato specifico. Quando si passa dalla Scena A alla Scena B, Transition Framework analizza la differenza e anima: cambiamento di posizione (ChangeBounds), cambiamento di dimensione (ChangeClipBounds, ChangeTransform), apparizione (Fade) e scomparsa (Fade).
import android.transition.Scene
import android.transition.TransitionManager
import android.transition.TransitionSet
import android.transition.AutoTransition
import android.transition.ChangeBounds
import android.transition.Fade
// TransitionSet: combinazione di ChangeBounds + Fade
val transition = TransitionSet().apply {
ordering = TransitionSet.ORDERING_TOGETHER
addTransition(ChangeBounds())
addTransition(Fade(Fade.IN))
duration = 400L
interpolator = FastOutSlowInInterpolator()
}
// Transizione tra scene in un ViewGroup
val sceneRoot = findViewById<ViewGroup>(R.id.scene_root)
val scene1 = Scene.getSceneForLayout(sceneRoot, R.layout.scene_initial)
val scene2 = Scene.getSceneForLayout(sceneRoot, R.layout.scene_expanded)
"> AutoTransition senza configurazione manuale
TransitionManager.go(scene2, AutoTransition().apply {
duration = 350L
})
// TransitionManager.beginDelayedTransition — senza scene
TransitionManager.beginDelayedTransition(sceneRoot, ChangeBounds())
itemView.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
itemView.requestLayout() "> La modifica del layout viene animata automaticamente
ChangeBounds è la transizione più utilizzata in Android, anima i cambiamenti di posizione e dimensione della View. ChangeTransform anima rotazione e scala. ChangeImageTransform — per l'animazione di ImageView (centerCrop → fitCenter). Per transizioni complesse con cambiamenti nella struttura gerarchica, utilizzate TransitionSet con ordering = ORDERING_SEQUENTIAL (le animazioni vengono eseguite in sequenza, non in parallelo). Secondo la documentazione di Android Developers (2026), Transition Framework supporta tutte le Views standard ma non supporta l'animazione di superficie (SurfaceView, TextureView, VideoView).
Domande frequenti
Animation — anima le proprietà di un singolo oggetto (alpha, translation, scale). Transition — anima il cambiamento di un insieme di oggetti o stati (fragment → fragment, scene → scene). Transition gestisce più cambiamenti contemporaneamente: apparizione, scomparsa, movimento, cambiamento di dimensione — e anima automaticamente la differenza tra due stati. Animation richiede la gestione manuale di ogni proprietà.
Utilizzate FragmentTransaction.setCustomAnimations con Transition Framework: fragment1.exitTransition = Fade(Fade.OUT), fragment2.enterTransition = Slide(Gravity.END). Per il sequenziamento: setReorderingAllowed(true) — i fragment verranno riordinati per una transizione parallela fluida. Per i casi semplici, slide_in_right / slide_out_left da R.anim è sufficiente. Per la navigazione indietro, specificate popEnter e popExit.
Transition Framework anima solo le proprietà che non attivano il ricalcolo del layout. Per l'animazione dell'altezza, utilizzate ChangeBounds — anima automaticamente i cambiamenti dei bordi della View tra due stati di layout. Chiamate TransitionManager.beginDelayedTransition(viewGroup, ChangeBounds()) prima di modificare LayoutParams. Se l'altezza cambia tramite WRAP_CONTENT, assicuratevi che layout_height sia diverso prima e dopo — ChangeBounds non funzionerà se entrambi gli stati sono match_parent.
Utilizzate il modificatore .transition() con AnyTransition: .transition(.move(edge: .trailing).combined(with: .opacity)). Per transizioni personalizzate, implementate AnyTransition.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)). Per l'animazione all'interno di NavigationStack, utilizzate .navigationTransition(.slide) su iOS 18+. SwiftUI non supporta UIViewControllerAnimatedTransitioning — solo transizioni dichiarative tramite AnyTransition.
Utilizzate Profile GPU Rendering (Android) o Core Animation Instrument (iOS). Per Android, attivate Transition Profiling tramite adb shell setprop debug.transition.profiling 1 — i log mostreranno la durata di ogni fase di transizione. Su iOS, attivate Slow Animations nel simulatore. Metriche target: durata < 400 ms, FPS >= 55, frame drop — non più di 2 per transizione. Per scene complesse con 15+ Views animate simultaneamente, utilizzate ChangeBounds con durata di 300 ms.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche