Wichtige Punkte
Timeline ist ein Animationszeitmodell, das definiert, wann eine Animation beginnt, wie lange sie dauert und wie der Zeitverlauf in Änderungen des animierten Parameters umgewandelt wird. Im Gegensatz zur Frame-für-Frame-Animation, bei der jeder Frame manuell festgelegt wird, wird die Timeline-Animation durch Parameter beschrieben: duration — Gesamtdauer, startDelay — Verzögerung vor dem Start, repeatCount — Anzahl der Wiederholungen und timingFunction — die Geschwindigkeitskurve.
Die Zeitachse arbeitet im normalisierten Raum [0, 1] — lokale Zeit oder Fortschritt. Der Wert Null entspricht dem Beginn der Animation, Eins dem Abschluss. Eine Easing-Funktion wandelt die lineare lokale Zeit in den tatsächlichen Animationsfortschritt um. Wenn repeatCount > 1 ist, zyklisiert die Zeitachse (RESTART) oder kehrt sich um (REVERSE). In iOS fügt CAMediaTiming speed (Wiedergabegeschwindigkeit, >1 beschleunigt) und timeOffset (Zeitachsenverschiebung) hinzu. In Android verwendet Animator setCurrentPlayTime() zur direkten Steuerung der Zeitachsenposition. Laut Material Design werden Animationen unter 100 ms nicht als Animationen wahrgenommen, während solche über 500 ms zu irritieren beginnen. Die optimale Dauer für Benutzeroberflächenanimationen beträgt 200–350 ms.
In iOS erfolgt die Verwaltung der Animationszeitachse über das CAMediaTiming-Protokoll, das von CAAnimation und CALayer implementiert wird. Es bietet die Eigenschaften: duration (in Sekunden), beginTime (Startzeit relativ zur übergeordneten Zeitachse), repeatCount (Anzahl der Wiederholungen), repeatDuration (Gesamtwiederholungsdauer), autoreverses (automatische Umkehrung), fillMode (Verhalten vor/nach der Animation), speed (Wiedergabegeschwindigkeit) und timeOffset (Zeitachsenverschiebung).
CACurrentMediaTime() ist eine Funktion, die die absolute Zeit in Sekunden seit dem letzten Geräteneustart zurückgibt. Sie wird als Basiszeitstempel zum Synchronisieren mehrerer Animationen verwendet: Setzen Sie beginTime = CACurrentMediaTime() + offset, um eine Gruppe von Animationen mit präziser Verzögerung zu starten. CAMediaTiming unterstützt auch die Zeitachsenhierarchie — eine übergeordnete Ebene kann alle untergeordneten Animationen über die speed-Eigenschaft beschleunigen/verlangsamen. In UIKit abstrahiert UIViewPropertyAnimator (iOS 10+) die Zeitachse über die Eigenschaften duration, delay und die Callbacks startAnimation/pauseAnimation/stopAnimation. In SwiftUI stellt die Animation-Struktur duration als Teil der Animationsspezifikation bereit, aber die direkte Zeitachsensteuerung ist verborgen — für eine benutzerdefinierte Zeitachse verwenden Sie TimelineView (iOS 15+) mit einem benutzerdefinierten Scheduler.
In Android ist die Zeitachsenverwaltung in der Klasse Animator und ihren Unterklassen (ValueAnimator, ObjectAnimator) zentralisiert. Die wichtigsten Zeitachseneigenschaften sind: duration (in Millisekunden), startDelay (Verzögerung vor dem Start), repeatCount (Anzahl der Wiederholungen), repeatMode (RESTART oder REVERSE). Die Methode setCurrentPlayTime(long) ermöglicht die Navigation an jede beliebige Position auf der Zeitachse.
ValueAnimator fügt eine Abstraktionsebene hinzu: Er generiert Werte von 0 bis 1 (normalisierte Zeit) basierend auf duration und übergibt sie an TimeInterpolator. AnimatorSet ermöglicht die Kombination mehrerer Animator-Instanzen in einer einzigen Zeitachse mit sequenzieller (playSequentially) oder paralleler (playTogether) Ausführung. In Jetpack Compose wird die Zeitachse über AnimationSpec verwaltet: tween (durationMillis, delayMillis, easing), spring (dampingRatio, stiffness) und keyframes (Punkte auf der Zeitachse mit benutzerdefiniertem Easing für jedes Segment). Compose AnimatedContent verwendet eine Zeitachse für transitionSpec — der Entwickler beschreibt, wie Inhalt mit zeitlichen Versätzen erscheint/verschwindet. Laut Android Performance sollte die Animationszeitachse ein Vielfaches von 16 ms (vsync-Zyklus) für 60fps sein — eine falsche Dauer kann aufgrund von Nichtübereinstimmung mit der Bildwiederholfrequenz des Displays zu Frame-Drops führen.
Die Zeitachse wird besonders wichtig bei der Komposition mehrerer Animationen. Drei grundlegende Muster: Kaskade — Animationen starten nacheinander mit einem Versatz (jede nächste beginnt nach einem Offset von der vorherigen), parallel — alle Animationen starten gleichzeitig mit derselben Zeitachse, und Stagger — Animationen starten überlappend (die zweite beginnt, bevor die erste abgeschlossen ist).
In iOS wird die Kaskade durch beginTime jeder CAAnimation = vorherige beginTime + vorherige duration implementiert. Für Stagger wird CAAnimationGroup mit unterschiedlichen beginTime-Werten bei verschachtelten Animationen verwendet. In Android unterstützt AnimatorSet playSequentially und playTogether; Stagger wird durch Setzen von startDelay für jeden Animator erstellt. In Compose verwendet AnimatedVisibility mit enter/exitTransition Stagger in der Variante staggerChildren für das kaskadierende Erscheinen von Listenelementen. Auf der WWDC und Google I/O 2024 stellten beide Plattformen Tools zur Zeitachsenvisualisierung vor: Xcode Animations Inspector und Android Studio Animations Timeline — sie zeigen die tatsächliche Zeitachse und verworfene Frames in Echtzeit.
Ein Beispiel für eine Animation mit benutzerdefinierter Zeitachse: 0,3 s Verzögerung, 0,6 s Dauer, automatische Umkehrung und unendliche Wiederholung. CAMediaTiming steuert alle Zeitparameter.
import UIKit
func breathingAnimation(layer: CALayer) {
let animation = CABasicAnimation(keyPath: "opacity")
animation.duration = 0.6
animation.beginTime = CACurrentMediaTime() + 0.3
animation.fromValue = 1.0
animation.toValue = 0.3
animation.autoreverses = true
animation.repeatCount = .infinity
animation.speed = 1.0
animation.fillMode = .both
layer.add(animation, forKey: "breathing")
}
beginTime = CACurrentMediaTime() + 0,3 sorgt für eine Verzögerung von 300 ms. autoreverses = true lässt die Animation nach jedem Zyklus rückwärts laufen. repeatCount = .infinity erzeugt eine Endlosschleife des „Atem“-Effekts. speed = 1,0 — normale Geschwindigkeit; beschleunigen Sie die Animation um das 2-fache mit einem Wert von 2,0.
SwiftUI TimelineView (iOS 15+) bietet Zugriff auf die Systemzeitachse für benutzerdefinierte Animationen. Die Ansicht wird bei jedem Tick des Schedulers aktualisiert.
import SwiftUI
struct TimelineViewExample: View {
var body: some View {
TimelineView(.periodic(from: .now, by: 0.1)) { context in
let seconds = context.date.timeIntervalSinceReferenceDate
let phase = sin(seconds * 2 * .pi)
Circle()
.fill(Color.blue)
.frame(width: 60 + 30 * phase)
.animation(.linear(duration: 0), value: phase)
}
}
}
TimelineView mit .periodic Scheduler erzeugt alle 0,1 s Ticks. Im Closure ist context.date die aktuelle Zeit, die in eine Sinuswellenphase umgewandelt wird. Die Kreisgröße pulsiert mit einer Frequenz von 1 Hz. TimelineView ist die einzige Möglichkeit, eine Animation mit eigener Zeitachse in SwiftUI ohne die Animation-API zu erstellen.
ValueAnimator mit vollständiger Zeitachsensteuerung: Verzögerung, Dauer, Wiederholung. Die Methode setCurrentPlayTime ermöglicht das Springen entlang der Zeitachse.
import android.animation.ValueAnimator
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.View
fun View.timelineAnimation() {
ValueAnimator.ofFloat(0f, 1f).apply {
duration = 400
startDelay = 200
repeatCount = 2
repeatMode = ValueAnimator.REVERSE
interpolator = AccelerateDecelerateInterpolator()
addUpdateListener { animator ->
val progress = animator.animatedFraction
alpha = progress
translationX = progress * 100f
}
start()
}
}
startDelay = 200 ms — Verzögerung vor dem Start. repeatMode = REVERSE setzt die Animation auf den Anfangswert zurück. animatedFraction gibt den Fortschritt von 0 bis 1 unter Berücksichtigung von repeatMode an. addUpdateListener feuert bei jedem Frame (normalerweise 16 ms). Verwenden Sie pause() / resume() zum Anhalten.
AnimatorSet ermöglicht die Erstellung komplexer Zeitachsen aus mehreren Animationen. Das Beispiel demonstriert die sequenzielle Ausführung mit Überlappung (Stagger).
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.view.View
fun View.staggerAnimation() {
val fadeIn = ObjectAnimator.ofFloat(this, "alpha", 0f, 1f).apply {
duration = 300
}
val slideUp = ObjectAnimator.ofFloat(this, "translationY", 100f, 0f).apply {
duration = 400
startDelay = 100
}
val scale = ObjectAnimator.ofFloat(this, "scaleX", 0.8f, 1f).apply {
duration = 250
startDelay = 200
}
AnimatorSet().apply {
playTogether(fadeIn, slideUp, scale)
start()
}
}
playTogether startet alle Animationen parallel, aber jede hat ihren eigenen startDelay — dies erzeugt einen Stagger-Effekt: fadeIn startet zuerst (0 ms), slideUp nach 100 ms, scale nach 200 ms. Die resultierende Zeitachse: 0 — 100 — 200 — 600 ms. Für sequenzielle Ausführung verwenden Sie playSequentially.
Häufig gestellte Fragen
Animator ist eine abstrakte Basisklasse für alle Animationen. ValueAnimator ist seine Unterklasse, die numerische Werte ohne Bindung an ein Objekt animiert. ObjectAnimator (eine Unterklasse von ValueAnimator) animiert eine bestimmte Eigenschaft eines Objekts (z. B. alpha oder translationY). Verwenden Sie für die Zeitachse ValueAnimator als universellen Timer mit einem Callback über addUpdateListener.
Verwenden Sie Systemuhren (CACurrentMediaTime in iOS, System.nanoTime in Android) zur Berechnung des Fortschritts, nicht die Frame-Anzahl. Für die Netzwerksynchronisierung verwenden Sie einen NTP-basierten Timer mit Offset-Korrektur. iOS unterstützt AVAudioSession für die Audio-Video-Zeitachsensynchronisation mit Millisekundengenauigkeit. Vermeiden Sie frame-basierte Synchronisierung — Bildwiederholraten variieren von 30 bis 120 fps.
startOffset ist die Verzögerung vor dem Animationsstart in Millisekunden. Wird für Kaskaden- und Stagger-Effekte verwendet. repeatCount ist die Anzahl der Wiederholungen: 0 = keine Wiederholung, ValueAnimator.INFINITE = Endlosschleife. repeatMode bestimmt die Richtung: RESTART (Zurücksetzen auf den Anfang) oder REVERSE (umgekehrte Richtung). In iOS heißt startOffset beginTime (in Kombination mit CACurrentMediaTime), repeatCount ist dieselbe Eigenschaft von CABasicAnimation.
Material Design empfiehlt 200–350 ms für Benutzeroberflächenanimationen. Animationen unter 100 ms werden nicht als Animationen wahrgenommen, während solche über 500 ms irritieren. Für den Ein-/Ausgang von Elementen: 200–300 ms. Für Übergänge zwischen Bildschirmen: 300–400 ms. Verwenden Sie kürzere Dauern für kleine Elemente (100–200 ms) und längere für große Elemente (400–500 ms).
Ruckeln (Jank) entsteht durch: langlaufende Operationen im Hauptthread (blockieren das Rendering), komplexe View/Layer-Hierarchie mit Overdraw oder falsche Zeitachsennutzung (Dauer kein Vielfaches von vsync — 16ms für 60fps, 8ms für 120fps). Verwenden Sie Xcode Instruments Animations oder Android Studio Profiler, um verworfene Frames zu erkennen. Verwenden Sie in Compose Modifier.drawWithCache zum Zwischenspeichern komplexer Zeichnungen.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch