Part three of this tutorial will guide you through creating a splash screen animation, an onboarding animation for an empty messages screen, turn-taking animations in chat messaging, and animating emojis. You’ll use our iOS Chat SDK sample application to get you up and running.
Since this is the final installment of our prototyping in SwiftUI series, you should check out part one and part two before diving in.
If you're unfamiliar with SwiftUI, also be sure to check out our SwiftUI Chat App tutorial.
Let’s get started.
Project Files to Get Started
The code for this tutorial is hosted on GitHub. You can clone the project’s repository, open it with Xcode( if you already have Xcode installed on your Mac), or download it as a ZIP file.
With the project files downloaded, you’re ready to start creating your launch screen animation.
Creating the Launch Screen Animation
The way your application launches impacts how users feel about the app. This section teaches you how to create a fast and seamless animated launch experience. When users tap your app icon, it will display the launch screen first as it starts. The launch screen is then replaced immediately with your app’s home screen.
The launch screen SwiftUI animation you will build in this section uses assets exported from Sketch. (Check out our seamless looping animations video on YouTube to learn how theses assets were structured in Sketch.)
In the Xcode assets library, you’ll find all the assets required to build this animation in the folder called LogoAnimation
. Create a new Swift file called SplashAnimation.swift and replace its content with the following code.
// // SplashAnimation.swift // StreamiOSChatSDKPrototyping // // Created by Amos from getstream.io on 14.10.2021. // import SwiftUI struct SplashAnimation: View { let StreamBlue = Color(#colorLiteral(red: 0, green: 0.368627451, blue: 1, alpha: 1)) // 1. Initial Animation States @State private var move = false @State private var swing = false @State private var splash = false var body: some View { if #available(iOS 15.0, *) { ZStack { StreamBlue .opacity(0.25) .ignoresSafeArea() ZStack { // 4. Logo: Splash Animation (scale and opacity) Image("streamLogo") .scaleEffect(0.6) .scaleEffect(splash ? 40 : 1) .opacity(splash ? 0 : 1) .rotationEffect(.degrees(swing ? -10 : 10), anchor: swing ? .bottomLeading : .bottomTrailing) .offset(y: -15) VStack(spacing: -46) { // 3. Top Wave: Horizontal Motion Image("stream_wave") .opacity(splash ? 0 : 1) .offset(y: 20) .offset(x: move ? -180 : 180) // 2. Bottom Wave: Horizontal Motion Image("stream_wave") .opacity(splash ? 0 : 1) .offset(y: 10) .offset(x: move ? -150 : 150) .onAppear { withAnimation(.easeInOut(duration: 1).repeatCount(8, autoreverses: true)){ swing.toggle() } withAnimation(.linear(duration: 4).speed(0.5)){ move.toggle() } withAnimation(.easeInOut(duration: 0.25).delay(8)){ splash.toggle() } } } .mask(Image("wave_top").opacity(0.9)) } // Change size here .scaleEffect(2) } } } struct SplashAnimation_Previews: PreviewProvider { static var previews: some View { SplashAnimation() } } }
To animate anything in SwiftUI, you have to change objects over time. To change objects over time, they must be driven by a state variable. When a state’s instance changes, it affects the layout, behavior, and contents of the view that has access to it.
To build the splash animation, we’ll use the move
, swing
, and splash
states. The move
state variable is used to create the horizontal motion of the waves (the two blue, wave-like rectangles). The swing
state is used to create the swinging effect of the logo, and splash
scales the logo up and makes all the views disappear at the end of the animation.
Follow the steps below to build the splash animation:
-
At the beginning of your struct, declare the following three states as
private
and set their initial states tofalse
. -
After using SwiftUI layout views to arrange the Stream logo and the waves as shown in the Swift file called SplashAnimation.swift, define how you want the animation to be triggered using the
.onAppear
modifier. The.onAppear
modifier is used to trigger animations automatically when the views it attaches to appear.- In the
.onAppear
modifier, add an explicit animation usingwithAnimation
and specify
the easing functions aseaseInOut
andlinear
. In SwiftUI, you can create implicit animations by attaching an animation modifier to the view you want to animate. Another way to add animations to a view is to use an explicit animation which animates views without attaching the animation to the views themselves. Visit Hacking with Swift to learn more about how to create implicit and explicit animations.
- In the
-
Set the duration and speed parameters for the easing equations. Then, add the state variable inside each animation and use
.toggle
to switch them between the initial and final animation states. -
Next, use a ternary conditional operation along with each state variable to animate the scale, opacity, rotation, offset, and anchor properties of the logo as well as the waves.
Using the sample code from the above, .scaleEffect(splash ? 40 : 1)
, you can see that the ternary operator takes three operands. The first, splash
, is a condition, followed by a question mark ?
, then a true expression 40
to execute, followed by a colon :
, and finally a false condition 1
to execute. The result of this sample code will scale the logo from its original size of 1
to its final size of 40
before it disappears since the initial state is false.
Following all steps above will create the splash animation. To preview the animation, click the play icon at the top of the Xcode preview.
Building the No Messages Onboarding Animation
Onboarding experiences help first-time users to get started with apps quickly. Your app’s onboarding animation should be purposeful and goal oriented. In this section, you will create an onboarding animation experience for chat participants when it is their first time to message each other.
After downloading the project from GitHub, open it with Xcode and look for the ChannelList folder. You will find the Swift file ChannelListEmptyView.swift, which contains the chat icon animation and the writing effect seen above. Copy all the content in the file and paste into a new Swift file.
// // SelectUserListView.swift // StreamiOSChatSDKPrototyping // // Created by Amos from getstream.io on 14.10.2021. // import SwiftUI struct ChannelListEmptyView: View { // 1. Animate From: Chaticon animations @State private var blinkLeftEye = true @State private var blinkRightEye = true @State private var trimMouth = false @State private var shake = false // 1. Animate From: Writing animation @State private var writing = false @State private var movingCursor = false @State private var blinkingCursor = false let cursorColor = Color(#colorLiteral(red: 0, green: 0.368627451, blue: 1, alpha: 1)) let emptyChatColor = Color(#colorLiteral(red: 0.2997708321, green: 0.3221338987, blue: 0.3609524071, alpha: 1)) var body: some View { VStack { HeaderView() Spacer() VStack { ZStack { Image("emptyChatDark") .rotationEffect(.degrees(shake ? -5 : 5), anchor: .bottomTrailing) VStack { HStack(spacing: 16) { RoundedRectangle(cornerRadius: 2) .frame(width: 8, height: 4) .scaleEffect(y: blinkRightEye ? 0.1 : 1) .opacity(blinkRightEye ? 0.1 : 1) RoundedRectangle(cornerRadius: 2) .frame(width: 8, height: 4) .scaleEffect(y: blinkLeftEye ? 0.05 : 1) } Circle() .trim(from: trimMouth ? 0.5 : 0.6, to: trimMouth ? 0.9 : 0.8) .stroke(style: StrokeStyle(lineWidth: 2, lineCap: .round, lineJoin: .round)) .frame(width: 16, height: 16) .rotationEffect(.degrees(200)) }.foregroundColor(emptyChatColor) .rotationEffect(.degrees(shake ? -5 : 5), anchor: .bottomLeading) } // 2. Animate To .onAppear { withAnimation(.easeInOut(duration: 1).repeatForever()){ blinkRightEye.toggle() } withAnimation(.easeOut(duration: 1).repeatForever()){ blinkLeftEye.toggle() } withAnimation(.easeOut(duration: 1).repeatForever()){ trimMouth.toggle() } withAnimation(.easeOut(duration: 1).repeatForever()){ shake.toggle() } // Writing Animation withAnimation(.easeOut(duration: 2).delay(1).repeatForever(autoreverses: true)) { writing.toggle() movingCursor.toggle() } // Cursor Blinking Animation withAnimation(.easeInOut(duration: 0.6).repeatForever(autoreverses: true)) { blinkingCursor.toggle() } } ZStack(alignment: .leading) { Text("Let’s start chatting!") .font(.body) .mask(Rectangle().offset(x: writing ? 0 : -150)) Rectangle() .fill(cursorColor) .opacity(blinkingCursor ? 0 : 1) .frame(width: 2, height: 24) .offset(x: movingCursor ? 148 : 0) } Text("How about sending your first message to a friend?") .font(.body) .foregroundColor(.secondary) .lineLimit(2) .multilineTextAlignment(.center) } Spacer() TabView { MentionsView() .tabItem { Label("Chats", image: "message_icon") } MentionsView() .tabItem { Label("Mentions", systemImage: "at") } }.frame(width: .infinity, height: 73) } // All Views } } struct ChannelListEmptyView_Previews: PreviewProvider { static var previews: some View { ChannelListEmptyView() .preferredColorScheme(.dark) } }
Follow the steps below to create any of the animations in this section. For example, to create the writing effect:
- Define the animation’s
From
value as@State private var writing = false
. This is the same as the initial state of the animation. - Next, specify how the animation should be initiated using the
.onAppear
modifier:.onAppear {withAnimation(.easeOut(duration: 2).delay(1).repeatForever(autoreverses: true)) { writing.toggle() }}
. - Then, place
withAnimation
inside the.onAppear
modifier and set the animation parameters such as the easing, duration, and delay. Add the.repeatForever
modifier to loop the animation infinitely. In addition, you should set theTo
value (final state) of the animation usingwriting.toggle()
. Thetoggle()
method is used here to switch between the false and true states of thewriting
variable. For each iteration, after the animation reaches its end value, it should return to the initial state. This is achieved by settingautoreverses
to true. - Finally, use the
writing
state variable to animate the offset (x-coordinate) of the rectangular mask which is overlaid on the text.mask(Rectangle().offset(x: writing ? 0 : -150))
. The text reveals and hides itself using the ternary conditional operation.
Building Animated Typing Indicators
In this section, you will animate turn-taking for chat messaging, one of the most common conversational cues used in face-to-face conversations. Turn-taking is easily discoverable for users because there are visual cues that signal to each participant that it is time to listen while the other participant speaks. In digital chat interfaces, this mechanism is known as a typing indication (“a user is typing…”).
You will create visual and animated cues that allow chat participants to take turns when making statements, asking questions and giving replies.
When using a chat messaging application, you will normally see an animation similar to the animations above which indicates the other user is about to send a message. Since the above animations are similar, you will create only one, which is the one that animates with opacity.
Create a new Swift file IsTypingOpacityView.swift or open the finished animation from the Animations folder after you download the project from GitHub to explore the code. The content of the animation is the same as the code below.
// // IsTypingOpacityView.swift // StreamiOSChatSDKPrototyping // // Created by Amos from getstream.io on 14.10.2021. // import SwiftUI struct IsTypingOpacityView: View { let notificationColor = Color(#colorLiteral(red: 1, green: 0.2156862745, blue: 0.2588235294, alpha: 1)) let onlineColor = Color(#colorLiteral(red: 0.1254901961, green: 0.8784313725, blue: 0.4392156863, alpha: 1)) @State private var isTyping = false var body: some View { HStack { ZStack(alignment: .topTrailing) { Image("user_chew") //User status: Online or offline Circle() .frame(width: 12, height: 12) .foregroundColor(onlineColor) } VStack(alignment: .leading){ Text("Opacity") .font(.body) HStack { HStack(spacing: 4) { Circle() .frame(width: 6, height: 6) .opacity(isTyping ? 1 : 0.1) .animation(.easeOut(duration: 1).repeatForever(autoreverses: true), value: isTyping) Circle() .frame(width: 6, height: 6) .opacity(isTyping ? 1 : 0.1) .animation(.easeInOut(duration: 1).repeatForever(autoreverses: true), value: isTyping) Circle() .frame(width: 6, height: 6) .opacity(isTyping ? 1 : 0.1) .animation(.easeIn(duration: 1).repeatForever(autoreverses: true), value: isTyping) } .onAppear{ isTyping.toggle() } Text("is typing") .font(.footnote) .lineLimit(1) .foregroundColor(.secondary) } } Spacer() VStack(alignment: .trailing) { // Number of unread messages Image(systemName: "2.circle") .foregroundColor(notificationColor) .font(.footnote) HStack(spacing: 4) { Image("readReceipt") Text("Now") .font(.footnote) .foregroundColor(.secondary) } } } } } struct IsTypingOpacityView_Previews: PreviewProvider { static var previews: some View { IsTypingOpacityView() .preferredColorScheme(.dark) } }
Follow these steps to create the is typing opacity
animation:
- Draw three circles and position them horizontally using an
HStack
layout container. - Define the initial animation state as
@State private var isTyping = false
, which is a boolean data type. - Attach the
.onAppear
modifier to the container holding all the circles to cue the animation when the circles appear. Also, toggle theisTyping
state variable so that the animation can switch between the false and true states. - Use the state variable along with the ternary conditional operation
.opacity(isTyping ? 1 : 0.1)
to animate the opacity of the circles. - Lastly, set the easing of the first circle to
easeOut
, the second circle toeaseInOut
, and the third circle toeaseIn
. Setting a different easing equation for each of the circles results in creating a sequential animation since the easing functions accelerate and decelerate at different times although they all have the same duration of one second. To make the animation loop back-and-forth indefinitely, append.repeatForever(autoreverses: true), value: isTyping)
to each of the easing equations.
Note: You can see from the code that each of the circles animates using the
.animation
modifier. Attaching the animation modifier directly to the circles this way will cause SwiftUI to animate any changes to the animatable properties of the circles. This is called an implicit animation.
Creating the Clapping Hands Animated Emoji
In this section, you will animate a round of applause. Popular messaging applications such as Skype and Telegram call it a “clapping hands” emoji.
Create a Swift file called ClappingHandsEmojiView.swift and replace its content with the following code or explore the code from the file in the folder AnimatedEmojis after you download the project from GitHub to see how it works.
// // ClappingHandsEmojiView.swift // ClappingHands // // Created by Amos Gyamfi from getstream.io on 5.11.2021. // import SwiftUI struct ClappingHandsEmojiView: View { // Initial Animation States @State private var blinking = false @State private var openingClosing = true @State private var clapping = true let StreamBlue = Color(#colorLiteral(red: 0, green: 0.368627451, blue: 1, alpha: 1)) var body: some View { VStack(alignment: .trailing) { ZStack { Image("head") VStack { ZStack { Image("eyelid") Image("eye_blink") // 1. Eye Blink Animation .scaleEffect(y: blinking ? 0 : 1) .animation(.timingCurve(0.68, -0.6, 0.32, 1.6).delay(1).repeatForever(autoreverses: false), value: blinking) } ZStack { Image("mouth") // 2. Mouth Opening Animation .scaleEffect(x: openingClosing ? 0.7 : 1) .animation(.timingCurve(0.68, -0.6, 0.32, 1.6).delay(1).repeatForever(autoreverses: true), value: openingClosing) HStack { Image("left_hand") // 3. Clapping Animation: Left Hand .rotationEffect(.degrees(clapping ? 15 : -5), anchor: .bottom) .offset(x: clapping ? 20 : -40) .animation(.easeInOut(duration: 0.2).repeatForever(autoreverses: true), value: clapping) Image("right_hand") // 4. Clapping Animation: Right Hand .rotationEffect(.degrees(clapping ? -15 : 5), anchor: .bottom) .offset(x: clapping ? -20 : 40) .animation(.easeInOut(duration: 0.2).repeatForever(autoreverses: true), value: clapping) } } } .onAppear{ // Final Animation States clapping.toggle() blinking.toggle() openingClosing.toggle() } }.frame(width: 58, height: 58) .scaleEffect(0.25) HStack(spacing: 4) { // Timestamp and read receipt Text("82") .foregroundColor(StreamBlue) Image("readReceipt") Text("18.37 PM") .foregroundColor(.secondary) } .font(.footnote) } } } struct ClappingHandsEmojiView_Previews: PreviewProvider { static var previews: some View { ClappingHandsEmojiView() .preferredColorScheme(.dark) } }
To create the clapping hand emoji animation, begin with the eye-blink animation:
-
Find all the assets in the assets library as shown below. They are contained in the folder,
ClappingHands
.
-
Define the following three initial animation states. The state variable
blinking
creates the eyeblink animation,openingClosing
is used for creating the mouth animation, andclapping
creates the clapping animation. -
Use SwiftUI’s depth stack (
ZStack
), vertical stack (VStack
) and horizontal stack (HStack
) to compose the emoji as shown in the finished code. -
Next, add the
Final Animation States
code snippet to the rootZStack
container housing all the animatable views so that the animation triggers automatically, and each final animation state can be toggled between true and false states: -
For the eye-blink animation, use the state variable
blinking
to scale the y-coordinate parameter of thescaleEffect()
modifier.
Note: The eyeblink animation uses a custom timing curve. To use custom cubic bezier curves for SwiftUI animations, visit easinggs.net. You can copy custom cubic bezier values and paste them directly into your SwiftUI animations’ timing curve.
The animation of the mouth is the same as the eye-blink animation, but opposite. Use the state variable openingClosing
to animate the x-coordinate parameter of the scale effect modifier.
To animate the hands, wrap them in a row container using HStack
. The right hand animation is opposite to the animation of the left hand. Here, you need to animate the angle rotation of each hand using the rotation effect modifier. Set the rotation to take effect from the bottom part of the hands by specifying the center gravity (anchor point) of the hands as bottom. To move the hands back-and-forth in the horizontal direction, animate the x-offset of the .offset
modifier using the clapping
state variable.
Creating the Revolving Hearts Emoji Animation
Animated emojis (emoticons) enrich chat messaging by adding affection, humor, and personal touches to key moments. In this section, you will learn how to create the revolving hearts emoji animation with SwiftUI. The revolving hearts emoji depicts a small heart and a large heart spinning (orbiting each other) on a circular motion path.
The revolving hearts emoji can be found in the folder ‘AnimatedEmojis’ with the file name RevolvingHeartsView. Its content is shown in the code below.
// // RevolvingHeartsView.swift // RevolvingHearts // // Created by Amos Gyamfi from getstream on 23.11.2021. // import SwiftUI struct RevolvingHeartsView: View { @State private var revolving = false var body: some View { VStack { VStack(spacing: 50) { ZStack { ZStack { Image("circular") Image("heart_top") // Rotation Mode: Do not rotate on path .rotationEffect(.degrees(revolving ? -360 : 360)) .offset(x: 10, y: -20) Image("heart_bottom") // Rotation Mode: Do not rotate .rotationEffect(.degrees(revolving ? -360 : 360)) .offset(x: -25, y: 20) } // Circular .rotationEffect(.degrees(revolving ? 360 : -360)) //.rotation3DEffect(.degrees(15), axis: (x: 3, y: 1, z: 0)) .animation(.easeInOut(duration: 5).repeatForever(autoreverses: false), value: revolving) .offset(x: 12.5, y: -20) .onAppear { revolving.toggle() } } } } } } struct RevolvingHeartsView_Previews: PreviewProvider { static var previews: some View { RevolvingHeartsView() .preferredColorScheme(.dark) } }
It consists of an image of a circular ring, a small heart image, and a large heart image. All the images can be found in the assets library in the folder called Emojis. Use column and depth stack containers to layout the views as shown in the code above.
You need only one animation state to create the revolving hearts emoji animation. Follow the steps below to create this animation:
-
Define an initial animation state called
revolving
and set it as a boolean data type using@State private var revolving = false
. -
Find the
ZStack
that is the direct parent of the image viewscircular
,heart_top
, andheart_bottom
. Attach the.onAppear
modifier to it as shown below. Then, add the state variable you defined and toggle its state so that it can switch between true and false. -
Add the rotation effect modifier and use the state variable to change the angle of rotation parameter with a ternary conditional operator:
.rotationEffect(.degrees(revolving ? 360 : -360))
-
Animate the views implicitly by attaching the
.animation
modifier, set duration, and how the animation should be repeated. Thevalue
parameter indicates the animation values to monitor for changes over time. It differs and it is dependent on the property you want to animate. In this case, the values to monitor are the angles of rotation 360° and -360°.
Following all the steps above will animate the revolving hearts emoji successfully.
Creating the “Like” Animation
Message reactions provide users in chat more playful and fun ways to react to how they feel about messages. In this section, you will create a heart burst animation and trigger it as a reaction. This animation could be used and triggered whenever users in a chat respond to messages to show that they like, love, or acknowledge something.
There are two heart icons used for this animation. A gray heart icon, which can be found in the assets library in the folder called ReactionIcons, and a pink icon, which is an SF Symbol. The other reaction icons beside the heart icon (which are not used in the animation) can be found in the same folder. The heart icons are displayed using the if
and else
condition called notLiked
. The if
statement shows the gray heart when it is not tapped. The pink heart only shows after the gray one has been tapped.
You can find the code for this animation in the Xcode project, after downloading it from GitHub. It is contained in the file ReactionsView.swift under the folder called LongPressGesture. The content of the file is the same as the code below.
// // ReactionsView.swift // Stream iOS Chat SDK Prototyping // // Created by Amos from getstream.io on 09.01.2022. // import SwiftUI struct ReactionsView: View { let reactionsBGColor = Color(#colorLiteral(red: 0.07058823529, green: 0.07843137255, blue: 0.0862745098, alpha: 1)) // Like Animation States @State private var notLiked = true @State private var removeInnerStroke = 14 @State private var chromaRotate = 0 @State private var animateTopPlus = 1 @State private var animateMiddlePlus = 1 @State private var animateBottomPlus = 1 var body: some View { ZStack { RoundedRectangle(cornerRadius: 28) .frame(width: 216, height: 40) .foregroundColor(reactionsBGColor) HStack(spacing: 20) { ZStack{ // When the heart icon is not tapped if notLiked { Image("like") } else { Image(systemName: "heart.fill") .font(.system(size: 24)) .frame(width: 24, height: 21) .foregroundColor(Color(.systemPink)) ZStack { Circle() .strokeBorder(lineWidth: CGFloat(removeInnerStroke)) .frame(width: 28, height: 28) .foregroundColor(Color(.systemPink)) .hueRotation(.degrees(Double(chromaRotate))) VStack { Image(systemName: "heart.fill") .scaleEffect(CGFloat(animateTopPlus)) .foregroundColor(Color(.systemPink)) Image(systemName: "plus") .scaleEffect(CGFloat(animateMiddlePlus)) Image(systemName: "heart.fill") .scaleEffect(CGFloat(animateBottomPlus)) .foregroundColor(Color(.systemPink)) } } } } .onTapGesture { withAnimation(.easeInOut(duration: 0.25)){ notLiked.toggle() } withAnimation(.easeOut(duration: 0.5)){ removeInnerStroke = 0 chromaRotate = 270 } withAnimation(.easeOut(duration: 0.5).delay(0.1)){ animateTopPlus = 0 } withAnimation(.easeInOut(duration: 0.5).delay(0.2)){ animateMiddlePlus = 0 } withAnimation(.spring()){ animateBottomPlus = 0 } } Image("thumbs_up") Image("thumbs_down") Image("lol") Image("wut_reaction") } } // All reaction views } } struct ReactionsView_Previews: PreviewProvider { static var previews: some View { ReactionsView() .preferredColorScheme(.dark) } }
To build the heart icon animation, start by defining the following animation start values. Each of the states (except notLiked
) is initialized with an integer. You need to change these integer values in the final animation state so that the animation can interpolate between the two values.
Like Animation States:
@State private var notLiked = true
@State private var removeInnerStroke = 14
@State private var chromaRotate = 0
@State private var animateTopPlus = 1
@State private var animateMiddlePlus = 1
@State private var animateBottomPlus = 1
- The
noLiked
state is used to swap the gray heart icon for the pink heart icon when you tap the gray one. - When the gray heart icon is tapped, the appearing and disappearing circle’s filling is removed. This is achieved using the state
removeInnerStroke
. - The
chromaRotate
state variable changes the hue values of the circle described above. - The
animateTopPlus
state displays and hides a plus icon on the top of the heart icon when it is tapped. - The
animateMiddlePlus
state displays a plus icon in front of the heart icon when it is tapped. - The
animateBottomPlus
shows a plus icon below the heart when the animation is triggered.
The heart animation should only start when you tap the gray heart icon. To create the tap action, add the .onTapGesture
modifier to the row layout container (HStack
) that houses all the reaction icons. Set the final animation states inside the .onTapGesture
modifier and define the following easing equations.
In the final animation states, you should set each state variable with an integer value. The individual animations for this animation do not occur at the same time. To make an animation start later in time, change its Begin Time
to a value greater than zero using the .delay
modifier.
All the views that animate when you tap the gray heart are embedded in an else
statement.
The final step to create this animation is to use all the animation states you have already defined to change the foreground color, size, and inner stroke of the views inside the else
statement as demonstrated in the code above.
Note: You need to convert the state variable used as the angle of rotation of the
.hueRotation
modifier to aDouble
data type and that of the.scaleEffect
modifier toCGFloat
for the animation to work. When you use the state state variable without converting it to the required data type, Xcode may throw an error with an option to fix it. Clicking on the fix button will change the state variable to the required data type.
Congratulations
This tutorial guided you through creating SwiftUI animations for chat messaging. You learned how to onboard users with animations, how to emulate turn-taking in chat with animations, and how to create animated emojis.
Download the GitHub project and explore the code used to create the animations in this tutorial, and be sure to show us what you're working on. If you'd like to incorporate our SwiftUI SDK into your own chat app, be sure to sign up for a free maker account to get started!
If you have any feedback regarding this tutorial, feel free to reach out at @getstream_io.
Happy coding!