Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.
Quelldateien prüfen
Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
.animation(.linear.repeatForever(autoreverses: true), value: ...) or .phaseAnimator
Rule: start with withAnimation. Reach for PhaseAnimator only when you have 3+ ordered states. Reach for only when you need parallel time-based tracks.
KeyframeAnimator
Springs (the only easing you should care about)
SwiftUI ships 4 named springs (iOS 17+). Use them. Tune response / dampingFraction only when a preset is wrong.
Preset (iOS 17+)
Equivalent
Mood
.snappy
.spring(response: 0.3, dampingFraction: 0.85)
UI snappy
.bouncy
.spring(response: 0.5, dampingFraction: 0.7)
playful
.smooth
.spring(response: 0.5, dampingFraction: 1.0)
calm, no bounce
.interactiveSpring()
.spring(response: 0.15, dampingFraction: 0.86)
gesture follow
response is the time the spring takes to settle (lower = snappier, higher = softer). dampingFraction is the overshoot intensity in 0...1 (1 = no overshoot, 0 = perpetual oscillation - never use 0). For UI work, stay in response: 0.2...0.5 and dampingFraction: 0.7...1.0. Deep-dive: references/springs-cheatsheet.md.
iOS 17+ also exposes .spring(duration:bounce:) where bounce is 0...1 (0 = critically damped, 1 = full bounce). It's the same spring, exposed in a more designer-friendly way:
Rule: prefer explicit (withAnimation) for state changes triggered by user actions; use implicit when any change to a value should always animate (e.g., a progress bar that updates from anywhere). Never both on the same property - the outer withAnimation wins, but the implicit .animation modifier still runs and stacks confusingly.
Transitions
Transitions drive insertion / removal of views inside an if, switch, or ForEach. They run when the parent's animation context fires (so wrap state mutations in withAnimation).
iOS 17+ also has the .transition(_:) modifier with custom transitions via the Transition protocol - useful for shared timing across many views. For 90% of work, the built-in combinators (.move, .opacity, .scale, .slide, .push, .asymmetric, .combined(with:)) are enough.
matchedGeometryEffect (hero animations)
Tag two views with the same id in the same Namespace. SwiftUI interpolates frame and position when the source view is replaced.
isSource: true (default on the source-of-truth view) tells SwiftUI which frame to interpolate from. Common gotchas: id collisions across unrelated namespaces, view identity instability (use stable ids, not array indices), and animating out of an if branch where the destination view doesn't exist yet (wrap both branches inside the same parent, use opacity to hide instead of removing).
PhaseAnimator (iOS 17+)
For ordered state choreography. Define a CaseIterable + Hashable enum, SwiftUI walks through phases sequentially, settling on the last one.
trigger: is optional - omit it to advance through phases automatically once on appear. Use it when you need an external signal (button tap, model update). Phases run sequentially, never in parallel - if you need parallelism, use KeyframeAnimator.
KeyframeAnimator (iOS 17+)
For continuous, time-based animations with parallel tracks. Each KeyframeTrack animates one keypath independently; SwiftUI runs them all together.
Four keyframe types: LinearKeyframe (constant velocity between points), SpringKeyframe (settles with spring), CubicKeyframe (cubic bezier ease), MoveKeyframe (jump cut, no interpolation). Trigger on a value change to re-run the animation. Deep-dive: references/phase-keyframe-deep.md.
Animatable / @Animatable
For custom drawing that needs interpolation. The @Animatable macro (iOS 17+) auto-synthesizes animatableData for any Equatable properties; the older Animatable protocol still works.
structProgressRing: Shape {
var progress: Double// 0...1var animatableData: Double {
get { progress }
set { progress = newValue }
}
funcpath(inrect: CGRect) -> Path {
var p =Path()
p.addArc(
center: CGPoint(x: rect.midX, y: rect.midY),
radius: rect.width /2,
startAngle: .degrees(-90),
endAngle: .degrees(-90+360* progress),
clockwise: false
)
return p
}
}
ProgressRing(progress: progress)
.stroke(.tint, lineWidth: 6)
.animation(.spring(.smooth), value: progress)
For multi-property shapes use AnimatablePair<A, B> (or nested pairs) as animatableData. The @Animatable macro removes that boilerplate when properties are Equatable + Animatable.
let tap =TapGesture().onEnded { print("tap") }
let drag =DragGesture().onChanged { value in offset = value.translation }
ZStack { ... }
.gesture(tap.simultaneously(with: drag))
Three composition operators: .simultaneously(with:) (parallel recognition), .sequenced(before:) (one must complete first), .exclusively(before:) (one or the other, not both). For fine-grained control over when child views can claim the gesture, use .simultaneousGesture(_, including: GestureMask) with .gesture, .subviews, .all, or .none. Deep-dive: references/gestures-swiftui.md.
Anti-Patterns (BAD / GOOD)
1. Deprecated .animation form (no value binding)
// BAD - implicit-anim-everywhere, deprecated in iOS 15+Circle().scaleEffect(scale).animation(.easeInOut)
// GOOD - bind to a specific valueCircle().scaleEffect(scale).animation(.easeInOut, value: scale)
// OR
withAnimation(.easeInOut) { scale =1.5 }
2. Animating frame size directly
// BAD - .frame() drives layout pass every frame, drops fps under loadCard().frame(height: expanded ?400 : 100)
.animation(.spring(), value: expanded)
// GOOD - animate transform-equivalents (scale, offset) that the compositor handlesCard()
.frame(height: 400)
.scaleEffect(expanded ?1 : 0.4, anchor: .top)
.animation(.spring(), value: expanded)
// OR for actual layout transitions, use matchedGeometryEffect
3. Scale to 0 (the vanish-into-nothing trap)
// BAD - element vanishes into a black hole, feels brokenCard().transition(.scale)
// GOOD - minimum scale 0.9-0.95 + opacityCard().transition(.scale(scale: 0.95).combined(with: .opacity))
4. withAnimation inside body
// BAD - body runs on every render, animation re-fires arbitrarilyvar body: someView {
let_= withAnimation(.spring()) { scale =1.2 } // never do thisCircle().scaleEffect(scale)
}
// GOOD - trigger from user actions or .onChangevar body: someView {
Circle()
.scaleEffect(scale)
.onTapGesture {
withAnimation(.spring()) { scale = scale ==1?1.2 : 1 }
}
}
Reduced motion respect
Mandatory. SwiftUI exposes the iOS / macOS "Reduce Motion" accessibility setting via the environment. See ../motion-principles/SKILL.md for the cross-platform rationale.
Rule: cross-fades and opacity are still allowed under reduced motion; large translations, scale-from-zero, parallax, and looping motion must be neutralized.