SwiftUI basic animation combat

SwiftUI is undoubtedly one of Apple’s most perfect masterpieces so far. By using SwiftUI, we can use more concise code to implement very complex functions.

Recently, I learned some knowledge about animation. With this latest framework, we can easily achieve very smooth animation effects.

The former picture is the demo I will guide you next. It is similar to the animation when we downloads the app from App Store, but it is different in some places. By tapping the enter button, the button will turn into a loading circle. When the system is finished loading, a check mark will appear to indicate the end of the animation.

If you want to know the details, you can refer to the GIF below.

Checkmark

Let’s first draw the animation of the check mark.

Create a new SwiftUI file, I named it CheckView, you can set it to any name.When drawing, we need to use the Path class.

The principle of drawing lines is actually very simple. In mathematics, we know that two points define a straight line. In order to draw the shape of the check mark, we need three points to achieve it. Since three points that are not on a straight line can determine a triangle, after drawing the triangle, we can successfully draw the shape of the checkmark by simply selecting its two sides.

Using the addLines(_ lines: [CGPoint]) function, you can add the points you want to place.

In order to better layout the checkmark, we need to use the GeometryReader drawing tool. GeometryReader, in Apple’s word, is a container view that defines its content as a function of its own size and coordinate space. This view returns a flexible preferred size to its parent layout.

Let’s put the checkmark in the center of the view.

Through this code, you will see a small purple checkmark appear on the simulator.

But how do we make it move?

We need a variable of type Bool, I set it to checkViewAppear. We need to declare this variable before the body.

@State private var checkViewAppear = false

Note that the .animation () function can add animation to the checkmark, so add this function behind our path and modify the parameters in .trim (from:, to :).

The instance method onAppear(perform:) gives you a chance to add an action to perform when this view appears.

Now tap the live preview and you will see the animation when checkmark appears.

Button View

Create a new SwiftUI View, I named it “ButtonAnimationView.swift”. Look at the view we will create later, it contains a rounded rectangle which as the background view, above the rounded rectangle is a HStack view which contains an image view and a text view. So let’s start from the ZStack view.

Here we create a rounded rectangle that shows in the following picture:

Now add the HStack view which contains an image view and a text view.

Now the button view has completed, we just need to add animations to the view. Let’s go back to the gif which has been showed before, we will see that after we tap the button, the view turn into the circle view, what’s more, the color of the circle view is different from the button view. To handle this change, we need a property wrapper type that can read and write a value which can change the view when the value has been written to another value. I set it downloadButtonTapped:

@State private var downloadButtonTapped = false

Now change the width of the button view when the button has been tapped.

RoundedRectangle(cornerRadius: 30)
···
.frame(width: self.downloadButtonTapped ? 60 : 300, height: 60)
···

After we tap the button, the background color of the view will ture into white and the foregroundColor will become purple

RoundedRectangle(cornerRadius: 30)
···
.foregroundColor(self.downloadButtonTapped ? .purple : Color(red: 230/255, green: 230/255, blue: 230/255))
.background(self.downloadButtonTapped ? .white : Color(red: 230/255, green: 230/255, blue: 230/255))
···

Now use instance method onTapGesture(count:perform:) to add an action to perform when rounded rectangle view recognizes a tap gesture.

RoundedRectangle(cornerRadius: 30)
···
.onTapGesture {
withAnimation(.default) {
self.downloadButtonTapped = true
}
}

To hide the HStack view after a tap gesture, we just need to chek whether the value of downloadButtonTapped is equal to true.

if !downloadButtonTapped {
HStack {
Image("riding").resizable().frame(width: 30, height: 30)
Text("Enter")
}.font(.headline)
}

Turn on the Live Preview, you will see the animation that shows how the rounded rectangle view turn into a circle view.

Now we need to make the circle missing a small part, and spinning up. Add two new property wrapper types:

@State private var loading = false
@State private var fullcircle = false

Change the following code to make the circle incomplete:

RoundedRectangle(cornerRadius: 30)
.trim(from: 0, to: self.fullcircle ? 0.95 : 1)
···

Add the instance method rotationEffect(_:anchor:) just before the onTapGesture method to rotate the circular view:

RoundedRectangle(cornerRadius: 30)
···
.rotationEffect(Angle(degrees: self.loading ? 0 : -1440))
···

And update the onTapGesture method:

RoundedRectangle(cornerRadius: 30)
···
.onTapGesture {
withAnimation(.default) {
self.downloadButtonTapped = true
self
.fullcircle = true
}
}

You maybe confused that the loading property is not contained in the onTapGesture method. Because the rotate animation is after the animation that the rounded rectangle view becomes the circle view, if you add the property to onTapGesture method, you will find that the animation is not what we need, and the duration of the animation is too short. To handle the circle rotate animation, we need a new function startProcessing(). Let’s create it:

struct ButtonAnimationView: View {
···
private
func startProcessing() {
withAnimation(Animation.linear(duration: 5)) {
self.loading = true
}
}
}

When to call this function? Apparently after the image view and text view disappear, so add the onDisappear method to the HStack view:

if !downloadButtonTapped {
HStack {
Image("riding").resizable().frame(width: 30, height: 30)
Text("Enter")
}.font(.headline)
.onDisappear() {
self.startProcessing()
}
}

Now you will see the rotate animation after tapping the rounded rectangle view.

Combine CheckView and Button View

Before the few seconds of the rotate animation, the CheckView will appear above the circle view. And they will complete even at the same time. So we need a new property wrapper type to show when the CheckView will show:

@State private var completed = false

In the startProcessing method, we assume the animation will be completed in 5 seconds, so we now assume after 4 seconds the CheckView will show:

struct ButtonAnimationView: View {
···
private
func startProcessing() {
withAnimation(Animation.linear(duration: 5)) {
self.loading = true
}
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
self.completed = true
self
.fullcircle = false
}
}
}

Now add the CheckView to the ZStack View:

if completed {
CheckView()
.offset(x: -5, y: 9)
.foregroundColor(.purple)
}

Now in the Live Preview, you will see the full animation.

Boot View

Create a new SwiftUI file called BootView.swift, check what will contain in this view. An image, two text views of different colors and the ButtonAnimationView.

It’s simple to create such view, so I just put the code here.

Demo Link

The whole project has been uploaded to Github, feel easy to download it.

👨‍🎓/study communication engineering🛠/love iOS development💻/🐶🌤🍽🏸🏫