This skill should be used when the user asks to "add animations", "implement gestures", "create micro-interactions", "animate transitions", "add pull-to-refresh", "implement swipe actions", "animate charts", or needs guidance on iOS animation patterns and interaction design.
From ios-dev-toolkitnpx claudepluginhub nbkm8y5/claude-plugins --plugin ios-dev-toolkitThis skill uses the workspace's default tool permissions.
references/animation_patterns.mdreferences/interaction_patterns.mdscripts/generate_animation_utilities.pyscripts/generate_chart_animations.pyscripts/generate_gesture_interactions.pySearches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Guides implementation of event-driven hooks in Claude Code plugins using prompt-based validation and bash commands for PreToolUse, Stop, and session events.
Generate comprehensive animation and interaction infrastructure for iOS applications. This skill provides battle-tested animation utilities, micro-interactions, gesture handlers, loading states, chart animations, and interactive components that make iOS apps feel professional and delightful.
python scripts/generate_animation_utilities.py <output_directory>
Generates:
AnimationUtilities.swift - Spring presets, timing curves, animation coordinatorMicroInteractions.swift - Button press, shake, pulse, bounce, shimmer effectsLoadingAnimations.swift - Spinners, progress bars, skeleton loadingpython scripts/generate_gesture_interactions.py <output_directory>
Generates:
GestureComponents.swift - Swipe actions, long press, drag to dismiss, pinch to zoomPullToRefresh.swift - Custom pull-to-refresh implementationBottomSheet.swift - Interactive bottom sheet with detentsCardInteractions.swift - Expandable, flippable, draggable cardspython scripts/generate_chart_animations.py <output_directory>
Generates:
ChartAnimations.swift - Animated bar, line, pie charts, progress rings, gaugesimport SwiftUI
struct MyButton: View {
var body: some View {
Text("Press Me")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
.buttonPress() // ✅ Add press animation
}
}
struct ContentList: View {
@State private var isRefreshing = false
var body: some View {
List(items) { item in
ItemRow(item: item)
}
.pullToRefresh(isRefreshing: $isRefreshing) {
await refreshData()
}
}
}
struct MessageRow: View {
var body: some View {
Text("Message content")
.padding()
.swipeActions(
leading: [
.init(icon: "pin", color: .blue, action: pinMessage)
],
trailing: [
.init(icon: "trash", color: .red, action: deleteMessage)
]
)
}
}
struct DataView: View {
@State private var isLoading = true
var body: some View {
VStack {
if isLoading {
LoadingSpinner()
} else {
DataContent()
}
}
}
}
// Or use skeleton loading
struct SkeletonView: View {
var body: some View {
VStack(alignment: .leading) {
Rectangle()
.frame(height: 20)
.skeleton()
Rectangle()
.frame(height: 60)
.skeleton()
}
.padding()
}
}
struct MultiStepForm: View {
@State private var currentPage = 0
var body: some View {
ZStack {
if currentPage == 0 {
Page1()
.transition(.slideAndScale)
} else if currentPage == 1 {
Page2()
.transition(.slideAndScale)
}
}
.animation(.naturalSpring, value: currentPage)
}
}
extension AnyTransition {
static var slideAndScale: AnyTransition {
.asymmetric(
insertion: .move(edge: .trailing).combined(with: .scale(scale: 0.9)),
removal: .move(edge: .leading).combined(with: .scale(scale: 0.9))
)
}
}
struct SalesChart: View {
let salesData: [AnimatedBarChart.BarData]
var body: some View {
AnimatedBarChart(
data: salesData,
barColor: .blue
)
.frame(height: 300)
}
}
struct ProgressView: View {
@State private var progress: Double = 0.75
var body: some View {
AnimatedProgressRing(
progress: progress,
color: .green
)
}
}
struct SuccessView: View {
@State private var celebrationTrigger = 0
var body: some View {
Image(systemName: "checkmark.circle.fill")
.font(.system(size: 60))
.foregroundColor(.green)
.successCelebration(trigger: celebrationTrigger)
.onAppear {
celebrationTrigger += 1
}
}
}
struct HomeView: View {
@State private var showSheet = false
var body: some View {
Button("Show Details") {
showSheet = true
}
.bottomSheet(
isPresented: $showSheet,
detents: [.medium, .large]
) {
DetailContent()
}
}
}
struct CardStack: View {
var body: some View {
DraggableCard(
onSwipeLeft: { handleReject() },
onSwipeRight: { handleAccept() }
) {
CardContent()
}
}
}
struct ValidationField: View {
@State private var text = ""
@State private var shakeCount = 0
var body: some View {
TextField("Email", text: $text)
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(8)
.shake(trigger: shakeCount)
.onSubmit {
if !isValid(text) {
shakeCount += 1
HapticStyle.error.trigger()
}
}
}
}
.naturalSpring - General UI transitions (response: 0.55, damping: 0.825).bouncySpring - Playful interactions (response: 0.6, damping: 0.7).snappySpring - Quick feedback (response: 0.3, damping: 0.9).smoothSpring - Professional apps (response: 0.7, damping: 1.0)Always respect reduce motion preferences:
Text("Animated Content")
.accessibleAnimation(.naturalSpring, value: state)
The skill automatically provides reduce motion support:
// Add animations to custom components
struct AnimatedCurrencyField: View {
@State private var amount: Decimal = 0
@State private var isFocused = false
var body: some View {
CurrencyTextField(value: $amount)
.scaleEffect(isFocused ? 1.05 : 1.0)
.animation(.naturalSpring, value: isFocused)
}
}
// Smooth page transitions in multi-step forms
struct AnimatedForm: View {
@StateObject private var formState = FormState()
var body: some View {
FormBuilder(state: formState) {
// Form pages with animated transitions
}
.transition(.asymmetric(
insertion: .move(edge: .trailing).combined(with: .opacity),
removal: .move(edge: .leading).combined(with: .opacity)
))
.animation(.naturalSpring, value: formState.currentPage)
}
}
// Animate chart updates when calculations change
struct MortgageChart: View {
let schedule: AmortizationSchedule
var body: some View {
AnimatedBarChart(
data: schedule.monthlyPayments.map { payment in
AnimatedBarChart.BarData(
label: payment.month,
value: payment.principalPaid
)
},
barColor: .blue
)
}
}
// Accessible animations that respect user preferences
struct AccessibleButton: View {
@State private var isPressed = false
var body: some View {
Button("Submit") {
// Action
}
.buttonPress()
.accessibleAnimation(.naturalSpring, value: isPressed)
.accessibilityHint("Double tap to submit")
}
}
For detailed patterns and examples, read the reference files:
These files contain:
ComplexView()
.optimizeAnimationPerformance() // Uses drawingGroup()
// Coordinate multiple animations
AnimationCoordinator.shared.chain(animations: [
(delay: 0.0, animation: .naturalSpring, action: { step1() }),
(delay: 0.3, animation: .bouncySpring, action: { step2() }),
(delay: 0.6, animation: .smoothSpring, action: { step3() })
])
All interactive components include appropriate haptic feedback:
HapticStyle.light.trigger() // Subtle feedback
HapticStyle.medium.trigger() // Standard feedback
HapticStyle.heavy.trigger() // Strong feedback
HapticStyle.success.trigger() // Success notification
HapticStyle.warning.trigger() // Warning notification
HapticStyle.error.trigger() // Error notification
HapticStyle.selection.trigger() // Selection change
.animation() with an explicit value binding@State or @Binding.drawingGroup() for complex animations.optimizeAnimationPerformance().accessibleAnimation() for user-facing animationsTotal: ~2,250 lines of production-ready animation code
Your animations should:
After implementing animations, consider: