swiftship/internal/skills/data/ui/animations/SKILL.md
Animation enforcement: containment, modifier order, timing, performance, and transition safety. Use when implementing UI patterns related to animations.
npx skillsauth add abdullah4ai/apple-developer-toolkit animationsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Enforce safe, performant animations that never escape their parent bounds.
CONTAINMENT (CRITICAL): Animated content inside a container (card, row, sheet, etc.) MUST NOT overflow its parent. Apply containment modifiers on the PARENT that clips:
// CORRECT — compositingGroup flattens, clipped constrains
CardContainer {
AnimatedContent()
.transition(.scale.combined(with: .opacity))
}
.compositingGroup()
.clipped()
// WRONG — animated child overflows parent during transition
CardContainer {
AnimatedContent()
.transition(.move(edge: .bottom))
}
// No containment — content renders outside CardContainer
WHY .compositingGroup().clipped():
WHEN TO APPLY CONTAINMENT:
WHEN CONTAINMENT IS NOT NEEDED:
MODIFIER ORDER:
// CORRECT — animation AFTER layout, containment on parent
VStack {
content
.offset(y: animating ? -20 : 0)
.opacity(animating ? 0 : 1)
.animation(.spring(duration: 0.4), value: animating)
}
.compositingGroup()
.clipped()
// WRONG — animation before layout modifiers
content
.animation(.spring, value: state)
.padding()
.frame(maxWidth: .infinity)
PREFER GPU TRANSFORMS:
// GOOD — GPU transform, no layout hit
Text("Hello")
.scaleEffect(isPressed ? 0.95 : 1.0)
.animation(.spring(duration: 0.2), value: isPressed)
// BAD — layout-driven animation
Text("Hello")
.padding(isPressed ? 10 : 16)
.animation(.spring, value: isPressed)
TIMING CURVES:
TRANSITIONS:
// CORRECT
withAnimation(.spring(duration: 0.3)) {
showDetail.toggle()
}
// In body:
if showDetail {
DetailView()
.transition(.opacity.combined(with: .move(edge: .bottom)))
}
// WRONG — animation inside the conditional
if showDetail {
DetailView()
.animation(.spring, value: showDetail) // too late
}
ANIMATION SCOPE:
// CORRECT — scoped to specific value
.animation(.easeInOut(duration: 0.2), value: isSelected)
// WRONG — unscoped, animates everything
.animation(.easeInOut)
LIST AND SCROLL ANIMATIONS:
List {
ForEach(items) { item in
ItemRow(item: item)
}
}
.animation(.spring(duration: 0.3), value: items.count)
PHASE AND KEYFRAME (iOS 17+):
// Phase animation with containment
PhaseAnimator([false, true]) { phase in
Icon()
.scaleEffect(phase ? 1.1 : 1.0)
.opacity(phase ? 1.0 : 0.7)
}
.compositingGroup()
.clipped()
testing
Use for 3D games: racing, 3D sports, board games, marble maze, tower defense, bowling. SceneKit + SceneView architecture, 3D scene hierarchy, physics, game loop, primitives, materials, cameras, particles, audio.
documentation
Game UI patterns: SwiftUI HUD overlays on SpriteKit, menus (main/pause/game-over), virtual joystick/d-pad, score displays, health bars, tutorial onboarding.
tools
Download free game sprites/textures/3D models and generate procedural assets. Covers nw_download_asset tool, texture factories, sprite atlas organization, 3D model loading, and programmatic asset creation.
testing
Use for 2D games: arcade, puzzle, sports, ping pong, platformer, shooter, 2D racing. SpriteKit + SpriteView architecture, scene hierarchy, physics, game loop, audio, particles, game feel.