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.
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!
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):
The specific code will be listed later. Because there is a feature that has not been mentioned here.
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)
)
})
}
}
}
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.
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~