主页 Swift UNIX C Assembly Go Plan 9 Web MCU Research Non-Tech

How to product new version UI animation after iOS 15 using SwiftUI

2022-08-13 | Swift | #Words: 1375

From iOS 15.0, Apple deprecates .animation(Animation?) and recommends developers to use .animation(Animation?, value: Equatable) or withAnimation. The possibilities and freedom of development are greater now.

But before go into the difference between the .animation(Animation?) and .animation(Animation?, value: Equatable) or withAnimation, you need to know some about UI animation. If you have known about that, you can skip next section.

What is animation?

Animation is a process of attribute changes, such as displacements, color changes, size changes.

In SwiftUI’s View, it is the effect produced by .offset, .foregroundColor, .frame and other properties gradually change. But if you change the variable directly, the properties will change immediately. It is very stiff and cannot be called as animation. Apple provides a very simple way to achieve animation, that is .animation.

It likes we set key frames, then set the intermediate frames to create animation!

Different styles of transition animation (intermediate frames)

Apple provides a very simple way, but perhaps it is too simple, so no one talks about it in detail.

There are many types of animation movement rhythms, such as slow in and slow out, constant speed movement. So here are all modes provided by Apple (if it is nil, it means no animation):

Comparison of different animation running speeds

The specific code will be listed later. Because there is a feature that has not been mentioned here.

Differences between the old and new versions

Now let’s talk about the difference between .animation(Animation?) and .animation(Animation?, value: Equatable) in detail.

The old version .animation(Animation?) is placed under the View that needs to move or change. When the View changes, a transition animation will appear. The transition animation includes attributes such as motion, style, color, etc.

The new version of .animation(Animation?, value: Equatable) has one more parameter than the old version, which is value. When the value specified changes, the transition Animation specified will be applied. The transition animation also includes attributes such as motion, style, color, etc. If other properties of the View change, no animation will occur.

Code for the animation is (the Chinese is due to my original blog is in Chinese):

struct AnimationView: View {
    @State private var offsetX: CGFloat = 150
    var body: some View {
        VStack {
            VStack {
                //default
                Text(".default(默认)")
                Rectangle()
                    .frame(width: 30, height: 30)
                    .offset(x: offsetX)
                    .foregroundColor(Color.pink)
                    /* `nil` is no animation */
                    .animation(.default, value: offsetX)
                
                // uniform motion
                Text(".linear(匀速)")
                Rectangle()
                    .frame(width: 30, height: 30)
                    .offset(x: offsetX)
                    .foregroundColor(Color.pink)
                    .animation(.linear, value: offsetX)
                
                // Slow out and fast in
                Text(".easeIn(开头缓慢,逐渐加速)")
                Rectangle()
                    .frame(width: 30, height: 30)
                    .offset(x: offsetX)
                    .foregroundColor(Color.pink)
                    .animation(.easeIn, value: offsetX)
                
                // faster out and slow in
                Text(".easeOut(结尾逐渐减速,缓慢结束)")
                Rectangle()
                    .frame(width: 30, height: 30)
                    .offset(x: offsetX)
                    .foregroundColor(Color.pink)
                    .animation(.easeOut, value: offsetX)
                
                // Slow out and slow in, the fusion above two animation
                Text(".easeInOut(缓出缓进,上面的融合体)")
                Rectangle()
                    .frame(width: 30, height: 30)
                    .offset(x: offsetX)
                    .foregroundColor(Color.pink)
                    .animation(.easeInOut, value: offsetX)
            }
            
            Button(action: {
                offsetX = -150
            }, label: {
                // Start
                Text("开始运动")
                    .padding()
                    .overlay(
                        RoundedRectangle(cornerRadius: 15)
                            .stroke(lineWidth: 3)
                    )
            })
        }
    }
}

How to make animation through multiple values?

Due to value, we just have animation when the value changes. How can we have animation when some values be changed?

If the values you want to observe are all in a same type, use an array, such as [offsetX, offsetY]. This is equivalent to observing whether the array has changed. As long as one value be changed, the array is changed at the same time. This can achieve the effect we want.

If the values you want to observe are not in a same type. Such as displacement and color, one is of type CGFloat and the other is of type Color. it may be very troublesome. So here are a few solutions for different cases:

First Solution Declare a data structure and then observe the data. Sometimes this is convenient for you to use this solution in some situations.

First Declare a new structure, the protocol is Equatable, as follows:

struct Test: Equatable {
     var offsetX: CGFloat
     var color: Color
}

Then comes the View part:

.animation(.default, value: Test(offsetX: offsetX, color: color))

This method is relatively versatile and can observe multiple changes, but it is a bit troublesome.

Since value comforms Equatable protocol, it means the value actually is a Bool value. Although we write value: color, it is actually judging whether this value has been changed, equivalent to color == Previous Color, through returning a Bool value (a classic C language programming skill, if you are familiar with C language it should be easy to understand).

But it is possible to gengerate animation when using a Bool value here. It is necessary to judge whether the value has changed.

So we can write it like this:

.animation(.default, value: offsetX == 150 && color == Color.pink)

This method is relatively simple, but since it only compares one value, it may be not useful in some cases.

Animation time length

We can set the duration of the transition animation to control the speed and rhythm of the animation. This is very simple. Many people may use the old version, but the new version is not officially mentioned and no one talks about it online, so I will talk about it here.

Just add (duration: duration) after the animation style. The unit of duration is in seconds.

// animation duration is 3 seconds
.animation(.linear(duration: 3), value: offsetX)

I hope these will help someone in need~