Huvudpunkter
Timeline — är tidsmodellen för animering som bestämmer när en animering börjar, hur länge den varar och hur tidsförloppet omvandlas till ändring av den animerade parametern. Till skillnad från bildruta-för-bildruta-animering (frame-by-frame), där varje bildruta ställs in manuellt, beskrivs timeline-animering av parametrar: duration — total varaktighet, startDelay — fördröjning före start, repeatCount — antal upprepningar och timingFunction — hastighetskurva.
Tidslinjen fungerar i normaliserat utrymme [0, 1] — local time eller progress. Värdet noll motsvarar början av animeringen, ett — slutförande. Easing-funktionen omvandlar linjär local time till faktiskt animationsframsteg. Om repeatCount > 1 återställs timeline cykliskt (RESTART) eller inverteras (REVERSE). I iOS lägger CAMediaTiming till parametrarna speed (uppspelningshastighet, >1 snabbar upp) och timeOffset (förskjutning av tidslinjen). I Android använder Animator setCurrentPlayTime() för direkt kontroll av position på tidslinjen. Enligt Material Design uppfattas inte animeringar kortare än 100 ms som animeringar, och längre än 500 ms börjar irritera. Optimal varaktighet för gränssnittsanimationer är 200–350 ms.
I iOS sker hanteringen av animationstidslinjen via protokollet CAMediaTiming, som implementeras av CAAnimation och CALayer. Det tillhandahåller egenskaperna: duration (varaktighet i sekunder), beginTime (starttid i förhållande till överordnad timeline), repeatCount (antal upprepningar), repeatDuration (total varaktighet för upprepningar), autoreverses (automatisk omvändning), fillMode (beteende före/efter animering), speed (uppspelningshastighet) och timeOffset (förskjutning på tidslinjen).
CACurrentMediaTime() — funktion som returnerar absolut tid i sekunder sedan senaste omstart av enheten. Används som grundläggande tidsstämpel för synkronisering av flera animeringar: ställ in beginTime = CACurrentMediaTime() + offset för att starta en grupp animeringar med exakt fördröjning. CAMediaTiming stöder även hierarki av timeline — den överordnade lagret kan snabba upp/sakta ner alla underordnade animeringar via egenskapen speed. I UIKit abstraherar UIViewPropertyAnimator (iOS 10+) tidslinjen via egenskaperna duration, delay och callbacks startAnimation/pauseAnimation/stopAnimation. I SwiftUI tillhandahåller strukturen Animation duration som en del av animationsspecifikationen, men direkt kontroll av tidslinjen är dold — för anpassad timeline använd TimelineView (iOS 15+) med egen scheduler.
I Android är hanteringen av tidslinjen centraliserad i klassen Animator och dess underklasser (ValueAnimator, ObjectAnimator). Huvudegenskaperna för timeline: duration (varaktighet i millisekunder), startDelay (fördröjning före start), repeatCount (antal upprepningar), repeatMode (RESTART eller REVERSE). Metoden setCurrentPlayTime(long) tillåter förflyttning längs tidslinjen till valfri position.
ValueAnimator lägger till en abstraktionsnivå: genererar värden från 0 till 1 (normalized time) baserat på duration och skickar dem till TimeInterpolator. AnimatorSet tillåter kombination av flera Animatorer till en enda tidslinje med sekventiell (playSequentially) eller parallell (playTogether) exekvering. I Jetpack Compose hanteras tidslinjen via AnimationSpec: tween (durationMillis, delayMillis, easing), spring (dampingRatio, stiffness) och keyframes (punkter på tidslinjen med anpassad easing för varje segment). Compose AnimatedContent använder timeline för transitionSpec — utvecklaren beskriver hur innehåll visas/försvinner med tidsförskjutningar. Enligt Android Performance bör animationstidslinjen vara en multipel av 16 ms (vsync-cykel) för 60fps — felaktig varaktighet kan orsaka bildruteförlust på grund av bristande överensstämmelse med skärmens uppdateringsfrekvens.
Tidslinjen blir särskilt viktig vid komposition av flera animeringar. Tre grundläggande mönster: kaskad — animeringar startas sekventiellt med förskjutning (varje nästa börjar efter offset från föregående), parallell — alla animeringar startas samtidigt med samma tidslinje och stagger — animeringar startas med överlappning (den andra börjar innan den första är klar).
I iOS implementeras kaskad via beginTime för varje CAAnimation = föregående beginTime + föregående duration. För stagger används CAAnimationGroup med olika beginTime i nästlade animeringar. I Android stöder AnimatorSet playSequentially och playTogether; stagger skapas genom att ställa in startDelay för varje Animator. I Compose använder AnimatedVisibility med enter/exitTransition stagger i varianten staggerChildren för kaskadvisning av listelement. På WWDC och Google I/O 2024 presenterade båda plattformarna visualiseringsverktyg för tidslinjen: Xcode Animations Inspector och Android Studio Animations Timeline — de visar den faktiska tidslinjen och missade bildrutor i realtid.
Exempel på animering med anpassad tidslinje: fördröjning 0.3 sek, varaktighet 0.6 sek, autoreverse och oändlig upprepning. CAMediaTiming hanterar alla tidsparametrar.
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 ger en fördröjning på 300 ms. autoreverses = true gör att animeringen går i motsatt riktning efter varje cykel. repeatCount = .infinity skapar en oändlig loop av „andningseffekten". speed = 1.0 — normal hastighet; snabba upp animeringen 2 gånger med värdet 2.0.
SwiftUI TimelineView (iOS 15+) ger tillgång till systemets tidslinje för anpassad animering. Vyn uppdateras vid varje tick från schedulern.
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 med .periodic scheduler genererar tick var 0.1 sek. I closure är context.date — aktuell tid som omvandlas till en sinusfas. Cirkelns storlek pulserar med frekvensen 1 Hz. TimelineView är det enda sättet att skapa en animering med egen tidslinje i SwiftUI utan Animation API.
ValueAnimator med full kontroll över tidslinjen: fördröjning, varaktighet, upprepning. Metoden setCurrentPlayTime tillåter hopp längs tidslinjen.
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 — fördröjning före start. repeatMode = REVERSE återför animeringen till ursprungsvärdet. animatedFraction ger framsteg från 0 till 1 med hänsyn till repeatMode. addUpdateListener utlöses varje bildruta (vanligtvis 16 ms). För paus använd pause() / resume().
AnimatorSet tillåter skapande av komplexa tidslinjer från flera animeringar. I exemplet — sekventiell exekvering med överlappning (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 startar alla animeringar parallellt, men varje har sin egen startDelay — detta skapar en stagger-effekt: fadeIn startar först (0 ms), slideUp efter 100 ms, scale efter 200 ms. Slutlig tidslinje: 0 — 100 — 200 — 600 ms. För sekventiell exekvering använd playSequentially.
Vanliga frågor
Animator — abstrakt basklass för alla animeringar. ValueAnimator är dess underklass som animerar numeriska värden utan bindning till ett objekt. ObjectAnimator (arvtagare till ValueAnimator) animerar en specifik egenskap hos ett objekt (t.ex. alpha eller translationY). För timeline använd ValueAnimator som universell timer med callback via addUpdateListener.
Använd systemklockor (CACurrentMediaTime i iOS, System.nanoTime i Android) för att beräkna framsteg, inte antalet bildrutor. För nätverkssynkronisering använd en NTP-baserad timer med offset-korrigering. iOS stöder AVAudioSession för synkronisering av audio-video tidslinje med millisekunders precision. Undvik synkronisering baserat på bildrutor — bildfrekvensen varierar från 30 till 120 fps.
startOffset — fördröjning före start av animering i millisekunder. Används för kaskad- och stagger-effekter. repeatCount — antal upprepningar: 0 = ingen upprepning, ValueAnimator.INFINITE = oändlig loop. repeatMode bestämmer riktning: RESTART (återställ till början) eller REVERSE (omvänd rörelse). I iOS kallas startOffset beginTime (i kombination med CACurrentMediaTime), repeatCount — samma egenskapsnamn i CABasicAnimation.
Material Design rekommenderar 200–350 ms för gränssnittsanimationer. Animeringar kortare än 100 ms uppfattas inte som animeringar, längre än 500 ms — irriterar. För in-/utgång av element: 200–300 ms. För övergångar mellan skärmar: 300–400 ms. Använd kortare varaktigheter för små element (100–200 ms) och längre för stora (400–500 ms).
Ryckighet (jank) uppstår på grund av: långvariga operationer i huvudtråden (blockerar rendering), komplex View/Layer-hierarki med overdraw, eller felaktig användning av timeline (varaktighet är inte en multipel av vsync — 16ms för 60fps, 8ms för 120fps). Använd Xcode Instruments Animations eller Android Studio Profiler för att upptäcka missade bildrutor. I Compose använd Modifier.drawWithCache för cachning av komplexa ritningar.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också