Journal

Duolingo-Style Progress Ring Animation on iOS (SwiftUI)

Two circles stacked, the top one trimmed to your progress, animated with a spring. That is the whole trick.

Duolingo-Style Progress Ring Animation on iOS (SwiftUI): the App Store logo as a frosted glass icon on a pink and blue gradient with bubbles

TL;DR

A Duolingo-style progress ring in SwiftUI is two stacked circles: a faint track and a colored progress arc drawn with Circle().trim(from: 0, to: progress), rotated so it starts at the top, with a rounded line cap. You animate it by changing the progress value inside withAnimation, and SwiftUI interpolates the trim, so you never redraw frames yourself. This is a learn-the-pattern guide, not a Duolingo asset. Start from a clean SwiftUI layout, like a free VP0 design at $0, and drop the ring into your streak or lesson screen.

The satisfying ring that fills as you finish a lesson looks complicated and is not. It is two stacked circles, the top one trimmed to your progress and animated. Once you see the pattern, you can build a progress ring for a streak, a goal, a timer, or a download in a few minutes. This is a learn-the-pattern guide to the SwiftUI technique, not a Duolingo asset to copy. Here is the code shape and the small details that sell it. Drop it into a clean screen from a free VP0 design (the free iOS and React Native design library AI builders read from) at $0.

The pattern: two circles, one trimmed

A progress ring is a ZStack of two circles. The back one is a faint full Circle stroked as the track. The front one is a Circle with .trim(from:to:), where progress is a value from 0 to 1, stroked in your accent color. That trim is what turns a full circle into an arc. Three details make it look right:

DetailModifierEffect
Start at the top.rotationEffect(.degrees(-90))Arc begins at 12 o’clock, not 3
Soft ends.stroke(style: StrokeStyle(lineCap: .round))The rounded Duolingo cap
Thick linelineWidth on both strokesA chunky, readable ring

By default a trimmed circle starts at the 3 o’clock position, which is why the -90 degree rotation matters. The rounded line cap is the small touch that separates a polished ring from a sharp, technical one.

Animating it without redrawing

Here is the part people overcomplicate: you do not animate frames. You change the progress value inside withAnimation, and SwiftUI interpolates the trim’s end point for you. A .spring() gives the lively, slightly bouncy fill that feels rewarding; an .easeOut gives a calmer one. Apple’s SwiftUI animation overview covers the timing curves. Because the shape is declarative, the same approach scales to a progress ring animation in SwiftUI for any metric, and it is the native cousin of creating animated React components easily, where you also describe states rather than frames. For the celebratory moment when the ring completes, pair it with something like the Duolingo streak flame animation.

Keep it honest: pattern, not clone

The circular progress control is a common pattern you are free to build. What is protected is Duolingo’s specific branding: the owl mascot, the exact color system, the sound design, and their assets. So build the technique, then style it as your own. If you are assembling a gamified screen, the Duolingo-style gamification UI assets discussion covers where that line sits. Keep your ring, your colors, your copy.

Driving it from real data

In a real app progress comes from somewhere: lessons completed over lessons total, minutes done over a daily goal, bytes downloaded over total. Compute that ratio, clamp it to 0 through 1, and feed it to the trim. Update it inside withAnimation whenever the underlying number changes, and the ring animates to the new value on its own. That is the entire data path, which keeps the view simple and testable.

The same restrained-spring craft applied to chat appears in the iMessage bubble physics build, where sent bubbles spring more eagerly than received ones.

Key takeaways

  • A progress ring is two stacked circles; the front one uses .trim(from: 0, to: progress).
  • Rotate the arc -90 degrees to start at the top, and use a round line cap for the soft look.
  • Animate by changing progress inside withAnimation; SwiftUI tweens the trim, no frames.
  • Build the pattern, not Duolingo’s branding; style the ring as your own.
  • Drop it into a clean VP0 streak or lesson screen at $0.

Frequently asked questions

How do I make a circular progress ring in SwiftUI?

Stack two circles. Draw a faint full Circle as the track, then a second Circle with .trim(from: 0, to: progress) and .stroke for the arc, where progress is 0 to 1. Rotate the arc by -90 degrees so it starts at the top, and use a round line cap for the soft Duolingo look. Animate by changing progress inside withAnimation and SwiftUI tweens the trim for you.

How do I animate the ring filling up?

Change the progress value inside a withAnimation block, or attach .animation to the trim. SwiftUI interpolates the trim’s end value over the duration, so the arc grows smoothly without you computing any frames. A spring animation gives the bouncy, lively feel; an easeOut gives a calmer fill.

Why is my SwiftUI ring starting from the wrong side?

By default a trimmed circle starts at the 3 o’clock position and goes clockwise. To start at the top like a progress ring, rotate the shape by -90 degrees with .rotationEffect(.degrees(-90)). If it animates the wrong direction, flip the trim range or the rotation accordingly.

You can build a circular progress ring; the control itself is a common UI pattern. What you should not copy is Duolingo’s exact branding, mascot, colors, and assets, which are protected. This guide teaches the SwiftUI technique so you can build your own ring with your own styling, not clone their app.

What is the best way to add a progress ring to an iOS app?

Build it from a trimmed Circle and animate the trim, then place it in a clean screen. A free VP0 design, the free iOS and React Native design library for AI builders, gives you the streak or lesson layout to drop the ring into and generate in Cursor or Claude Code at $0.

What the VP0 community is asking

How do I make a circular progress ring in SwiftUI?

Stack two circles. Draw a faint full Circle as the track, then a second Circle with .trim(from: 0, to: progress) and .stroke for the arc, where progress is 0 to 1. Rotate the arc by -90 degrees so it starts at the top, and use a round line cap for the soft Duolingo look. Animate by changing progress inside withAnimation and SwiftUI tweens the trim for you.

How do I animate the ring filling up?

Change the progress value inside a withAnimation block, or attach .animation to the trim. SwiftUI interpolates the trim's end value over the duration, so the arc grows smoothly without you computing any frames. A spring animation gives the bouncy, lively feel; an easeOut gives a calmer fill.

Why is my SwiftUI ring starting from the wrong side?

By default a trimmed circle starts at the 3 o'clock position and goes clockwise. To start at the top like a progress ring, rotate the shape by -90 degrees with .rotationEffect(.degrees(-90)). If it animates the wrong direction, flip the trim range or the rotation accordingly.

Is it legal to copy Duolingo's progress ring?

You can build a circular progress ring; the control itself is a common UI pattern. What you should not copy is Duolingo's exact branding, mascot, colors, and assets, which are protected. This guide teaches the SwiftUI technique so you can build your own ring with your own styling, not clone their app.

What is the best way to add a progress ring to an iOS app?

Build it from a trimmed Circle and animate the trim, then place it in a clean screen. A free VP0 design, the free iOS and React Native design library for AI builders, gives you the streak or lesson layout to drop the ring into and generate in Cursor or Claude Code at $0.

Part of the UI Animations, Gamification & Microinteractions hub. Browse all VP0 topics →

Keep reading

Wheel of Fortune Spinner UI Template for iOS: the App Store logo as a frosted glass icon on a pink and blue gradient with bubbles
Guides 4 min read

Wheel of Fortune Spinner UI Template for iOS

A free SwiftUI spinner pattern: a smooth spin that lands fairly on a segment, with disclosed odds, Reduce Motion support, and no pay-to-spin. A reward, not gambling.

Lawrence Arya · June 2, 2026
Leaderboard Podium Animation for iOS (Free SwiftUI Pattern): the App Store logo as a glossy glass icon on a purple and blue gradient with floating bubbles
Guides 5 min read

Leaderboard Podium Animation for iOS (Free SwiftUI Pattern)

Build a leaderboard podium that ranks your top three with a satisfying rise-into-place animation in SwiftUI, accessible and free, starting from a VP0 design.

Lawrence Arya · May 31, 2026
Confetti Cannon Animation in SwiftUI (Free Pattern): the App Store logo on a glass tile over a blue gradient with bubbles
Guides 4 min read

Confetti Cannon Animation in SwiftUI (Free Pattern)

A confetti cannon celebrates a real win with a burst of particles. Build it in SwiftUI, fire it only on genuine success, and respect Reduce Motion.

Lawrence Arya · May 31, 2026
Duolingo-Style Streak Flame Animation in SwiftUI: a glossy App Store icon on a blue, pink and orange gradient with bubbles
Guides 4 min read

Duolingo-Style Streak Flame Animation in SwiftUI

Build a Duolingo-style streak flame animation in SwiftUI: a flame that grows with the streak and celebrates a daily win, from a free VP0 design.

Lawrence Arya · May 31, 2026
Meditation Breathing Circle Animation in SwiftUI: the App Store logo on a glass tile over a blue gradient with bubbles
Guides 4 min read

Meditation Breathing Circle Animation in SwiftUI

Build a calming breathing-guide animation in SwiftUI: a circle that expands and contracts to pace breathing, with haptics, from a free VP0 design.

Lawrence Arya · May 31, 2026
Rive Interactive Button in React Native: the App Store logo on a glass tile over a blue gradient with bubbles
Guides 4 min read

Rive Interactive Button in React Native

Build an interactive button with Rive in React Native: idle, press, loading, and success states in one animation, from a free VP0 design.

Lawrence Arya · May 31, 2026