In iOS 14, Apple introduced many new features to the SwiftUI framework, such as LazyVGrid and LazyHGrid. MatchedGeometryEffect is a compelling feature, which allows developers to create gorgeous view animations with just a few strokes of code. The SwiftUI framework already makes it easy for developers to use animations to render view changes, and the matchedGeometryEffect modifier takes view animations to another level.

With all mobile apps, we often need to switch between multiple views, so a pleasing view switch can definitely improve the overall user experience. There are the [matchedGeometryEffect](https://developer.apple.com/documentation/swiftui/view/matchedgeometryeffect(id:in:properties:an Chor :issource:), you only need to describe the appearance of the two views, and the decorator automatically calculates the differences between the views and automatically animates the size and position changes.

This may be confusing for you, but don’t worry, you’ll understand what I’m talking about when I introduce the entire sample App.

** This article is excerpted from the book Self-mastering SwiftUI. If you want to learn more about SwiftUI animation and SwiftUI framework, please purchase the complete book from AppCoda.

Revisit the SwiftUI animation

Before we get started on matchedGeometryEffect, let’s take a look at how to implement animations using SwiftUI. The following image shows the start and end states of a view. When you click on the circular view on the left, it should get bigger and move up; Instead, when you click on the view on the right, it returns to its original size and position.

Implementing this clickable circular view is very simple. After starting a new SwiftUI project, update the ContentView structure like this:

struct ContentView: View {
    @State private var expand = false
    var body: some View {
        Circle()
            .fill(Color.green)
            .frame(width: expand ? 300 : 150, height: expand ? 300 : 150)
            .offset(y: expand ? -200 : 0)
            .animation(.default)
            .onTapGesture {
                self.expand.toggle()
            }
    }
}
Copy the code

We use a state variable expand to record the current state of the Circle view. When the state changes, we modify the size and offset of the frame with the modifiers.frame and.offset. If you execute the App in the preview screen, you should be able to see the animation when you click on the circular view.

Know the matchedGeometryEffect decorator

So what is a matchedGeometryEffect? How does this feature simplify the steps of implementing view animation? Let’s look again at the first image and the code for the circular view animation. We need to find the exact numerical difference between the start and end states. In this case, the size and displacement of the view box.

With the matchedGeometryEffect decorator, you no longer need to find the difference between two states. You only need to describe two views: one is the start state and the other is the end state, matchedGeometryEffect automatically adds the size and position difference between the two views.

To create the same animation as before using matchedGeometryEffect, you need to declare a namespace variable:

@Namespace private  var shapeTransition
Copy the code

Then, rewrite the body section like this:

var body: some View {
    if expand {
        // Final State
        Circle()
            .fill(Color.green)
            .matchedGeometryEffect(id: "circle", in: shapeTransition)
            .frame(width: 300, height: 300)
            .offset(y: -200)
            .animation(.default)
            .onTapGesture {
                self.expand.toggle()
            }
    } else {
        // Start State
        Circle()
            .fill(Color.green)
            .matchedGeometryEffect(id: "circle", in: shapeTransition)
            .frame(width: 150, height: 150)
            .offset(y: 0)
            .animation(.default)
            .onTapGesture {
                self.expand.toggle()
            }
    }
}
Copy the code

In this code, we create two circular views, one for the start state and one for the end state. When it is initially initialized, we get a Circle view centered and 150 points wide. When the expand state variable changes from False to true, the App displays another Circle view that is 200 points up from the middle and 300 points wide.

For both Circle views, we add the matchedGeometryEffect decorator and specify the same ID and namespace. With this setup, SwiftUI can calculate the size and position difference between two views and add view transitions. With the subsequent addition of the Animation decorator, the SwiftUI framework automatically animates view transformations.

The purpose of the ID and namespace is to mark views that belong to the same transformation, so both Circle views will use the same ID and namespace.

We’ve shown how to implement an animated transform between two views using matchedGeometryEffect. If you’ve ever used Magic Move in Keynote, this new decorator is very similar to Magic Move. I recommend that you use the iPhone emulator to execute the App to test the animation. As I write this article, there is a bug in Xcode 12 that prevents us from testing this animation in the preview screen.

From a circle to a rounded rectangle

Now, let’s try implementing another view transition animation. This time, we will change a rounded rectangle into a Rounded rectangle. The circle is at the top of the screen and the rounded rectangle is at the bottom of the screen.

We can use the technique we just learned to prepare two views: a circular view and a rounded rectangle view, and the matchedGeometryEffect decorator handles the part of the view transformation. Now, let’s change the ContentView structure of the body variable to this:

VStack {if expand {RoundedRectangle Spacer() RoundedRectangle(cornerRadius: 50.0). MatchedGeometryEffect (id: RoundedRectangle Spacer) "circle", in: shapeTransition) .frame(minWidth: 0, maxWidth: .infinity, maxHeight: 300) .padding() .foregroundColor(Color(.systemGreen)) .animation(.easeIn) .onTapGesture { expand.toggle() } } else { // Circle RoundedRectangle(cornerRadius: 50.0). MatchedGeometryEffect (id: "Circle ", in: shapeTransition). 100, height: 100) .foregroundColor(Color(.systemOrange)) .animation(.easeIn) .onTapGesture { expand.toggle() } Spacer() } }Copy the code

We also use the expand state variable to switch between the circular view and the rounded rectangle view. This code is very similar to the previous example, except that we have added VStack and Spacer here to locate the view. Why, you might ask, use RoundedRectangle to create round objects? The main reason is that it makes the view transition smoother.

In both views, we add the matchedGeometryEffect decorator, and specify the same ID and namespace, and we are done. The decorator automatically compares the differences between the two views and animates the changes. If you run the App on a preview screen or an iPhone emulator, you’ll see the view transition perfectly between circles and rounded rectangles. That’s the power of matchedGeometryEffect.

However, you may notice that this decorator does not perform animations that change colors. Yep, matchedGeometryEffect can only handle position and size changes.

Practice a

Let’s do a little exercise to test your knowledge of matchedGeometryEffect. Your task is to create an animated view transformation as shown below. It starts as an orange circle that, when clicked, transforms into a full-screen background. You can find the complete code in the project file.

Use animated transformations to swap two views

Now that you have a basic understanding of matchedGeometryEffect, let’s go ahead and look at how it helps us create some gorgeous animations. In this example, we will swap the position of the two circular views and apply modifiers to create a smooth view transition.

We use a state variable to store the swapped state and create a namespace variable for matchedGeometryEffect to use. Let’s declare these parameters in the ContentView:

@State private  var swap = false

@Namespace private  var dotTransition
Copy the code

The orange circle is on the left of the screen and the green circle is on the right. When the user clicks on any of the circles, it triggers an interchangeable animation. When you use MatchedGeo Archive, you don’t have to know how the swap animation is achieved. To set up a view transformation, you only need to do the following:

  1. Create the orange and green circles before swapping the layout
  2. Create two circles after swapping layout configuration

If you want to convert layout configuration to code, you can write the body variable like this:

if swap {
    // After swap
    // Green dot on the left, Orange dot on the right
    HStack {
        Circle()
            .fill(Color.green)
            .frame(width: 30, height: 30)
            .matchedGeometryEffect(id: "greenCircle", in: dotTransition)
        Spacer()
        Circle()
            .fill(Color.orange)
            .frame(width: 30, height: 30)
            .matchedGeometryEffect(id: "orangeCircle", in: dotTransition)
    }
    .frame(width: 100)
    .animation(.linear)
    .onTapGesture {
        swap.toggle()
    }
} else {
    // Start state
    // Orange dot on the left, Green dot on the right
    HStack {
        Circle()
            .fill(Color.orange)
            .frame(width: 30, height: 30)
            .matchedGeometryEffect(id: "orangeCircle", in: dotTransition)
        Spacer()
        Circle()
            .fill(Color.green)
            .frame(width: 30, height: 30)
            .matchedGeometryEffect(id: "greenCircle", in: dotTransition)
    }
    .frame(width: 100)
    .animation(.linear)
    .onTapGesture {
        swap.toggle()
    }
}
Copy the code

We used HStack to arrange the two circles horizontally and Spacer to create some space between the two circles. When the swap is set to true, the green circle is placed to the left of the orange circle. Conversely, the green circle will be placed to the right of the orange circle.

As you can see, we just need to describe the configuration of a circular view in different states, and matchedGeometryEffect takes care of the rest. We add a modifier to each Circle view, but this time it’s a little different, because we have two different Circle views that need to be configured, and we use two different ids to build the matchedGeometryEffect modifier. We identified the orangeCircle as orangeCircle and the greenCircle as grecle.

Now, if you run the App in the simulator, you should be able to switch animations whenever you click on any of the circles.

Exercise 2

In the previous exercise, we used matchedGeometryEffect on two circles and swapped their positions. This exercise will use the same technique, but this time with two images. The image below shows an example of this. When the swap button is clicked, the App will exchange the two images with a beautiful animation.

Feel free to use your own images. I used these free images from Unsplash.com in my example:

  • Unsplash.com/photos/pMW4…
  • Unsplash.com/photos/PM4V…

conclusion

The implementation of view animations is taken to another level with the introduction of the matchedGeometryEffect decorator. You can create beautiful view animations with less code. Even if you’re new to SwiftUI, you can benefit from this new modifier and make your App even better.

Recommended at the end of the article: iOS popular anthology

  • 1.BAT and other major manufacturers iOS interview questions + answers

  • 2.Must-read books for Advanced Development in iOS (Classics must Read)

  • 3.IOS Development Advanced Interview “resume creation” guide

  • (4)IOS Interview Process to the basics