Hello there, I'm Vini.
I have been working with mobile development for the past 11 years. For as long as I can remember, I have enjoyed creating products that are meaningful and enjoyable to users. When coding an app, I don’t just focus on its components—what excites me is delivering an enjoyable experience to the users as a whole.
For example, TMRW, a social payment app, is more than just handling money transactions for me. It's about how the users feel throughout the process, ensuring they never get bored. I always aim to take it a step further by introducing small fun aspects that increase engagement—like animations.
Recently, I added a confetti explosion to the app whenever users react with emojis, simulating a celebration. Confetti exploding, emojis flying, and I almost added Nicolas Cage's face to join the party! It’s just an animation, but it makes interactions more fun and lively each time someone reacts.
It's all about enhancing user experiences and impressing them, even in a payment app. Here’s how I did it...
Before jumping into the code, here’s a quick preview of what we’ll be building:
📱 A simple post feed with interactive reaction buttons.
🎉 Emoji explosions that animate dynamically when tapped.
This is a simple project focused on demonstrating the animation, so the structure is straightforward.
├── EmojiExplosion/
│ ├── Assets.xcassets
│ ├── EmojiExplosionApp.swift
│ ├── Models
│ │ ├── Emoji.swift
│ │ └── Post.swift
│ └── Views
│ ├── EmojiExplosionView.swift
│ ├── FeedView.swift
│ └── PostView.swift
Nothing fancy, but it keeps things organized and easy to follow.
File: Models/Post.swift
Since we're building a simple social feed, let's define a Post
object to represent each post.
import Foundation struct Post: Identifiable { let id: Int let content: String }
File: Views/PostView.swift
This step creates a basic layout for the post card. We’ll include a profile picture, name, handle, time, post content, and reaction buttons—all mocked for simplicity.
We break down the components for better readability, and the PostView
will trigger the emoji explosion when a reaction button is pressed.
import SwiftUI struct PostView: View { let post: Post let onReaction: (String) -> Void var body: some View { VStack(alignment: .leading, spacing: 8) { profileSection actionButtons } .padding() .background(Color(.systemBackground)) .cornerRadius(12) .shadow(color: Color(.systemGray4), radius: 3, x: 0, y: 1) } private var profileSection: some View { HStack(alignment: .top) { Image(systemName: "person.circle.fill") .resizable() .frame(width: 40, height: 40) .foregroundColor(.gray) VStack(alignment: .leading, spacing: 4) { Text("John Doe") .font(.headline) Text("@johndoe • 2h") .font(.subheadline) .foregroundColor(.gray) Text(post.content) .font(.body) .padding(.top, 2) } } } private var actionButtons: some View { HStack { ForEach(reactionData, id: \.emoji) { reaction in reactionButton( emoji: reaction.emoji, label: reaction.label, systemImage: reaction.systemImage ) Spacer() } } .padding(.top, 6) .foregroundColor(.gray) } private func reactionButton(emoji: String, label: String, systemImage: String) -> some View { Button(action: { onReaction(emoji) }) { HStack(spacing: 4) { Image(systemName: systemImage) Text(label) .font(.subheadline) } } } private var reactionData: [(emoji: String, label: String, systemImage: String)] { [ (emoji: "👍", label: "Like", systemImage: "hand.thumbsup.fill"), (emoji: "❤️", label: "Love", systemImage: "heart.fill"), (emoji: "🔥", label: "Super Like", systemImage: "flame.fill"), ] } } #Preview { PostView(post: .init(id: 0, content: "Hey")) { _ in } }
File: Views/FeedView.swift
This file is responsible for displaying the feed. It lists the posts and triggers the emoji explosion when a reaction is tapped. The actual emoji explosion will also happen here.
import SwiftUI struct FeedView: View { let posts = [ Post(id: 1, content: "Had the best coffee today! ☕️"), Post(id: 2, content: "Just finished a 5k run 🏃♂️"), Post(id: 3, content: "Vacation vibes 🌴"), ] var body: some View { ZStack { ScrollView { ForEach(posts) { post in PostView( post: post, onReaction: { emoji in // Trigger the emoji explosion } ) .padding(.horizontal) .padding(.top, 8) } } } } } #Preview { FeedView() }
File: Models/Emoji.swift
The Emoji
model represents each individual emoji in the explosion. It contains the emoji’s symbol, position, scale, opacity, and animation properties.
import SwiftUI struct Emoji: Identifiable, Equatable { let id = UUID() var symbol: String var position: CGPoint var scale: CGFloat var opacity: Double var animation: Animation }
File: Views/EmojiExplosionView.swift
Now, let's create the animation that makes the emojis explode on the screen. Here’s how it works step by step.
EmojiExplosionView
We define the EmojiExplosionView
, which holds and animates the emojis.
import SwiftUI public struct EmojiExplosionView: View { public let emoji: String @State private var emojis: [Emoji] = [] public init(emoji: String) { self.emoji = emoji }
emoji
variable: The emoji that will explode is passed when the view is initialized.@State emojis
: This holds an array of Emoji
objects representing each animated piece of the explosion.Each emoji is shown in a random size and position within a ZStack
, animated as it explodes across the screen.
public var body: some View { ZStack { ForEach(emojis) { emojiConfetti in Text(emojiConfetti.symbol) .font(.system(size: CGFloat.random(in: 20 ... 50))) // Random font size for variety .position(emojiConfetti.position) // Position on the screen .scaleEffect(emojiConfetti.scale) // Random scale effect to simulate distance .opacity(emojiConfetti.opacity) // Opacity for fading effect .animation(emojiConfetti.animation, value: emojis) // Apply animation } } .onAppear { withAnimation { explodeConfetti() // Call explosion animation when view appears } } }
The explodeConfetti
function generates 50 emojis with random positions and scales, sending them flying across the screen. Each emoji animates with an ease-out effect, making the explosion look smooth.
private func explodeConfetti() { // Center of the screen let centerX = UIScreen.main.bounds.width / 2 let centerY = UIScreen.main.bounds.height / 2 // Generating Emoji Confetti for _ in 0 ..< 50 { // Random X and Y let randomX = CGFloat.random(in: -200 ... 200) let randomY = CGFloat.random(in: -200 ... 200) // Initial and Target Positions let position = CGPoint(x: centerX, y: centerY) let targetPosition = CGPoint(x: centerX + randomX, y: centerY + randomY) // Ease-Out Animation let emojiConfetti = Emoji( symbol: emoji, position: position, scale: 1.0, opacity: 1.0, animation: .easeOut(duration: 1) .delay(Double.random(in: 0 ... 0.2)) ) emojis.append(emojiConfetti) // Animating the Explosion DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { if let index = emojis.firstIndex(of: emojiConfetti) { // Update Properties emojis[index].position = targetPosition emojis[index].scale = CGFloat.random(in: 0.5 ... 1.5) emojis[index].opacity = 0 } } } // Remove Emojis DispatchQueue.main.asyncAfter(deadline: .now() + 2) { emojis.removeAll() } }
centerX
, centerY
), where the explosion will originate.randomX
, randomY
) within a range of -200 to 200 points.File: Views/FeedView.swift
Now that we have the feed, post model, and emoji explosion view, it’s time to put them together. Each post in the feed will trigger the explosion when a reaction button is pressed.
// // FeedView.swift // EmojiExplosion // // Created by Vinícius Salmont on 07/09/24. // import SwiftUI struct FeedView: View { // State properties @State private var selectedEmoji = "" @State private var showExplosion = false let posts = [ Post(id: 1, content: "Had the best coffee today! ☕️"), Post(id: 2, content: "Just finished a 5k run 🏃♂️"), Post(id: 3, content: "Vacation vibes 🌴"), ] var body: some View { ZStack { ScrollView { ForEach(posts) { post in PostView( post: post, onReaction: { emoji in // Set the new states selectedEmoji = emoji DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { showExplosion = true } } ) .disabled(showExplosion) // Disable the interaction .padding(.horizontal) .padding(.top, 8) } } .blur(radius: showExplosion ? 10 : 0) // Show blur // Show Emoji Explosion. if showExplosion { Color.black.opacity(0.001) .ignoresSafeArea() EmojiExplosionView(emoji: selectedEmoji) .transition(.opacity) .onAppear { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { showExplosion = false } } } } .animation(.easeInOut, value: showExplosion) } } #Preview { FeedView() }
We’ve built a fun and simple emoji confetti explosion effect that enhances the user experience. It's a delightful addition to your app, making interactions more enjoyable.
Next up: Let's add haptics to enhance the experience even further!
Happy coding! 🎉
Vinícius Salmont