A SwiftUI library for creating beautiful staggered animations with minimal code.
This project is based on the objc.io Swift Talk episode "Staggered Animations Revisited".
- 🌊 Simple API: Add staggered animations with just a single view modifier
- 🔄 Customizable: Control animation timing, order, and transitions
- 📱 Accessibility: Respects reduced motion settings
- 🧩 Composable: Works with any SwiftUI transition
- 🔍 Smart sorting: Order by position, priority, or custom criteria
Add the following to your Package.swift
file:
dependencies: [
.package(url: "https://github.com/ivan-magda/swiftui-stagger-animation.git", from: "1.0.0")
]
Or add it in Xcode:
- Go to File → Add Packages...
- Paste the repository URL:
https://github.com/ivan-magda/swiftui-stagger-animation.git
- Click "Add Package"
VStack {
ForEach(items) { item in
ItemView(item: item)
.stagger() // Default opacity transition
}
}
.staggerContainer() // Required to coordinate animations
// Single transition
Text("Hello").stagger(transition: .move(edge: .leading))
// Combined transitions
Image(systemName: "star")
.stagger(transition: .scale.combined(with: .opacity))
// Setting animation priority (higher values animate first)
Text("First").stagger(priority: 10)
Text("Second").stagger(priority: 5)
Text("Third").stagger(priority: 0)
// Configure stagger container
VStack {
// Your views...
}
.staggerContainer(
configuration: StaggerConfiguration(
baseDelay: 0.1, // Time between each item
animationCurve: .spring(response: 0.5),
calculationStrategy: .priorityThenPosition(.topToBottom)
)
)
// Available calculation strategies
.priorityThenPosition(.leftToRight) // Default
.priorityOnly
.positionOnly(.topToBottom)
.custom { lhs, rhs in
// Your custom sorting logic
}
// Available directions
.leftToRight
.rightToLeft
.topToBottom
.bottomToTop
// Available animation curves
.default
.easeIn
.easeOut
.easeInOut
.spring(response: 0.5, dampingFraction: 0.8)
.custom(Animation.interpolatingSpring(mass: 1, stiffness: 100, damping: 10))
struct ContentView: View {
@State private var isVisible = false
let colors: [Color] = [.red, .orange, .yellow, .green, .blue, .purple]
var body: some View {
VStack(spacing: 16) {
Text("Stagger Animation Demo")
.font(.largeTitle)
.stagger(
transition: .move(edge: .top).combined(with: .opacity),
priority: 10
)
LazyVGrid(columns: [GridItem(.adaptive(minimum: 80))], spacing: 16) {
ForEach(colors.indices, id: \.self) { index in
RoundedRectangle(cornerRadius: 12)
.fill(colors[index])
.frame(height: 80)
.stagger(transition: .scale.combined(with: .opacity))
}
}
Button("Reset") {
isVisible.toggle()
}
.padding()
}
.padding()
.staggerContainer(
configuration: StaggerConfiguration(
baseDelay: 0.08,
animationCurve: .spring(response: 0.6)
)
)
}
}
- iOS 17.0+ / macOS 14.0+ / tvOS 17.0+
- Swift 6.0+
- Xcode 15.0+
Contributions are welcome! Please feel free to submit a Pull Request.
Stagger is available under the MIT license. See the LICENSE file for more info.