Controlled Animations
↗ Controlling the timing and movements of your animations
.phaseAnimator()
swift
// » SwiftUI Garden
// » https://swiftui-garden.com/Animations/Controlled-Animations
import SwiftUI
struct PhaseAnimatorExample: View {
@State private var trigger = false
enum Phase: CaseIterable {
case small, large
}
var body: some View {
VStack {
Image(systemName: "star.fill")
.font(.system(size: 50))
.phaseAnimator(
[Phase.small, .large],
trigger: trigger,
content: { content, phase in
let color = switch phase {
case .small: Color.blue
case .large: Color.orange
}
let scale = switch phase {
case .small: 0.5
case .large: 1.2
}
let yOffset = switch phase {
case .small: 0.0
case .large: -40.0
}
content
.foregroundStyle(color)
.scaleEffect(scale)
.offset(y: yOffset)
},
animation: { phase in
switch phase {
case .small: .easeIn(duration: 0.2)
case .large: .spring(duration: 0.5, bounce: 0.4)
}
}
)
.frame(height: 150)
Button("Animate", systemImage: "play.fill") {
trigger.toggle()
}
.buttonStyle(.borderedProminent)
}
}
}
#Preview {
PhaseAnimatorExample()
}
.keyframeAnimator()
swift
// » SwiftUI Garden
// » https://swiftui-garden.com/Animations/Controlled-Animations
import SwiftUI
struct KeyframeAnimatorExample: View {
@State private var trigger = false
struct AnimationValues {
var scale = 1.0
var rotation = Angle.zero
var yOffset = 0.0
}
var body: some View {
VStack {
Image(systemName: "star.fill")
.font(.system(size: 50))
.foregroundStyle(.orange)
.keyframeAnimator(
initialValue: AnimationValues(),
trigger: trigger,
content: { content, value in
content
.scaleEffect(value.scale)
.rotationEffect(value.rotation)
.offset(y: value.yOffset)
},
keyframes: { _ in
KeyframeTrack(\.scale) {
LinearKeyframe(1.5, duration: 0.2)
SpringKeyframe(0.8, spring: .bouncy)
CubicKeyframe(1.2, duration: 0.3)
LinearKeyframe(1.0, duration: 0.2)
}
KeyframeTrack(\.rotation) {
LinearKeyframe(.degrees(45), duration: 0.3)
CubicKeyframe(.degrees(-30), duration: 0.4)
SpringKeyframe(.degrees(360), spring: .snappy)
}
KeyframeTrack(\.yOffset) {
LinearKeyframe(-30, duration: 0.2)
SpringKeyframe(20, spring: .bouncy(duration: 0.4))
CubicKeyframe(-10, duration: 0.2)
LinearKeyframe(0, duration: 0.3)
}
}
)
.frame(height: 150)
Button("Animate", systemImage: "play.fill") {
trigger.toggle()
}
.buttonStyle(.borderedProminent)
}
}
}
#Preview {
KeyframeAnimatorExample()
}