SymbolParticleMorph is a SwiftUI package that renders SF Symbols as animated particle fields and morphs the particles when the symbol changes. It started as the thumbs-up/thumbs-down symbol morph used in PodPosture, but the package API is generic and app independent.
Licensed under the MIT License.
- PodPosture - Posture Improver uses
SymbolParticleMorphfor its onboarding and posture-state SF Symbol particle glyphs.
PodPostureDemo.mp4
- iOS 18+
- macOS 15+
- Swift 6.1+
The package uses SwiftUI Canvas and ImageRenderer.
Add the package with Swift Package Manager:
.package(url: "https://github.com/AndreasInk/SymbolParticleMorph.git", from: "0.2.0")Then add SymbolParticleMorph to your app target.
import SwiftUI
import SymbolParticleMorph
struct ThumbToggle: View {
@State private var liked = true
var body: some View {
Button {
liked.toggle()
} label: {
SymbolParticleMorph(
symbolName: liked ? "hand.thumbsup" : "hand.thumbsdown",
configuration: ParticleMorphConfiguration(quality: .balanced)
)
.frame(width: 220, height: 220)
.accessibilityLabel(liked ? "Positive" : "Negative")
}
.buttonStyle(.plain)
}
}SymbolParticleMorph(
symbolName: "figure.stand",
configuration: ParticleMorphConfiguration(quality: .compact, contentInset: 4)
)
.frame(width: 56, height: 56)
.accessibilityHidden(true)The Examples/SymbolParticleMorphExample iOS project demonstrates the morph view with quality and rendering controls. It includes a launch screen storyboard so the app can run on iOS 27 devices and be profiled in Instruments.
ParticleMorphConfiguration controls:
maxParticleCount: upper bound for sampled particles.samplingStep: pixel stride used while sampling the rasterized symbol.contentInset: padding before mapping particles into the view.particleSizeRange: rendered particle size range.revealDuration: initial reveal animation duration.frameBudget: number of timer ticks after each symbol change.frameRate: requested animation frame rate.renderingStyle:.hierarchical,.monochrome, or.palette.primaryColor: primary color used when rasterizing the SF Symbol.secondaryColor: secondary color used by.paletterendering.symbolPointSize: size used while rasterizing the SF Symbol.
Use .compact for small status glyphs, .balanced for medium icon art, and .detailed for large hero glyphs.
Particle count is the main cost driver. Prefer .compact for toolbar or status glyphs, and keep detailed morphs on larger static surfaces. The package caches rasterized SF Symbols by symbol name, render size, scale, rendering style, colors, and point size. You can warm the cache:
await MainActor.run {
SymbolParticleMorphCache.preload(
symbols: ["hand.thumbsup", "hand.thumbsdown", "figure.stand"],
configuration: ParticleMorphConfiguration(quality: .compact)
)
}The view honors the system Reduce Motion setting by skipping the motion ticks and rendering the stable particle shape. Add an accessibility label at the call site when the symbol communicates state.
- Blank output usually means the SF Symbol name is invalid or the frame is zero-sized.
- Choppy animation usually means the particle cap is too high for the surface. Try
.compactor increasesamplingStep. - If colors look unexpected, try a different
renderingStyle.
SymbolParticleMorph is available under the MIT License. See LICENSE.