How to Pause and Resume Animation in SwiftUI (with chaining)
SwiftUI doesn’t have built-in functionality to pause/resume animations. The same is true for animation chaining. So let’s see how we can solve this
Concept
Stop/Resume animations
To stop the animation, we can use a zero-length animation and the current value of the property being animated
withAnimation(.easeInOut(duration: 0)) {
propertyValue = propertyCurrentValue
}
To resume just run the same animation but the duration must be calculated as totalDuration-(pausedTime-startedTime)
withAnimation(.easeInOut(duration: remainingTime)) {
propertyValue = propertyFinalValue
}
Such logic can be encapsulated in an AnimatableModifier and easily applied to any property we want to stop/resume
.offset(x: xOffset)
.modifier(PauseModifier(
propertyValue: $xOffset,
propertyFinalValue: 100,
duration: 0.5
))
Chaining
Imagine we have a complex animation consisting of many animations with different durations, intervals, start and end times
But they can be anchored to the “parent” animation timeline. In this case, Total Duration is the duration of the complex animation. The Current Time is a kind of player timer. When we pause a player, it stops counting, if we press play it will keep counting
Each animation (AnimatableModifier) should calculate or receive the Current Time value and run if the Current Time == startTime. I think updating the Current Time (scheduling) is a good job for a manager. Summarising, updating the Current Time is necessary at the pause and startTime of each animation
Solution
PauseModifierManager was created to manage complex animations. You can use multiple managers on one screen. The current implementation loops animations, but it’s easy to modify. maxTime can be bigger than Total Duration depending on your needs
@ObservedObject private var animationManager = PauseModifierManager(maxTime: 5)
“Child” animations must be created by the manager
.modifier(animationManager.modifier(
propertyValue: $xOffset,
propertyFinalValue: 100,
startTime: 2.3,
endTime: 3.1
))
The illustration below describes how the PauseModifierManager works internally
Welcome to read the source code!