Jumping Ball Animation in SwiftUI

Problem Build a SwiftUI animation in which a ball repeatedly jumps — moving up and down to simulate a bounce — demonstrating command of SwiftUI's state and animation APIs.

Requirements

  • A Circle() view whose vertical position animates between a low and a high point
  • The bounce repeats indefinitely without manual timer management
  • @State private var isJumping: Bool (or an offset value) as the single source of truth
  • Animation driven declaratively from state, not by imperatively mutating the view

Core design

  • A @State property tracks the ball's vertical offset (or a boolean the offset is derived from). Changing state is the entire animation trigger — SwiftUI diffs the view tree and interpolates the changed value.
  • Apply .offset(y: isJumping ? -100 : 0) to the Circle(), then attach .animation(.easeInOut(duration: 0.5).repeatForever(autoreverses: true), value: isJumping).
  • autoreverses: true gives the down-swing for free — the animation plays forward then backward, so only one state edge is needed rather than two alternating writes.
  • Kick it off in .onAppear { isJumping = true }. The single state flip plus repeatForever sustains the loop; no timer, no completion handler, no manual re-trigger.
  • withAnimation { } is the alternative entry point when the change is driven by an event (a tap) rather than a value the view already observes.
  • Easing choice carries the physics: .easeInOut reads as a soft float, while .easeOut up and .easeIn down better mimics gravity — the ball should decelerate at the apex and accelerate into the floor.

Discussion points

  • Declarative vs. imperative: SwiftUI renders from state and interpolates between snapshots, whereas UIKit typically drives frames through CADisplayLink/timers or UIView.animate blocks. The SwiftUI version has no frame loop to leak or invalidate.
  • .animation(value:) vs. the deprecated implicit .animation(_:) — the value-scoped form only animates that specific change, avoiding accidental animation of unrelated view updates.
  • repeatForever never completes, so any completion handler never fires — worth knowing before relying on one.
  • Trade-off: .offset moves the rendered view without affecting layout (cheap, neighbors don't shift); animating frame or padding re-runs layout and can drag surrounding views along.
  • Realism extension: squash-and-stretch via .scaleEffect at contact, and a shadow that shrinks as the ball rises, both driven off the same state.
  • For physics rather than easing curves, .spring() or a TimelineView with a gravity computation gives genuine acceleration instead of an interpolated approximation.
asked …
LeaderboardSalaryAccount