To animate something SwiftUI you have a couple of choices, implicit and explicit animations. Implicit animations are done view view modifiers .animation(.easeIn)
and explicitly ones are declare inside the animation block withAnimation
.
I often use explicit animation and therefore I am always setting the values of a State property inside the animation block.
struct ExplicityAnimationExample: View {
@State var scaleSquare = false
var body: some View {
Rectangle()
.fill(Color.red)
.frame(width: 100, height: 100)
.scaleEffect(scaleSquare ? 2 : 1)
.onAppear {
withAnimation(.spring()) {
scaleSquare.toggle()
}
}
}
}
The same thing can be achieved by implicitly adding the animation after the scaleEffect modifier
struct ExplicityAnimationExample2: View {
@State var scaleSquare = false
var body: some View {
Rectangle()
.fill(Color.red)
.frame(width: 100, height: 100)
.scaleEffect(scaleSquare ? 2 : 1)
.animation(.spring())
.onAppear {
scaleSquare.toggle()
}
}
}
This saves you wrapping the content inside the withAnimation
block.
The more views you have that require animation the more state changes and view modifiers you will need.
struct ExplicityAnimationExample3: View {
@State var scaleSquare = false
@State var scaleSquare2 = false
var body: some View {
VStack(spacing: 150) {
Rectangle()
.fill(Color.red)
.frame(width: 100, height: 100)
.scaleEffect(scaleSquare ? 2 : 1)
.animation(.spring())
Rectangle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.scaleEffect(scaleSquare ? 2 : 1)
.animation(.spring().speed(0.5))
}
.onAppear {
scaleSquare.toggle()
scaleSquare2.toggle()
}
}
}
I find it particular difficult to maintain this is in a nice way, due to the fact that you change state in one place and you have to find the the corresponding animation that matches said change.
So I created a new property wrapper to solve this problem.
I call it AnimatedState.
You can declare the state and animation that you want like so:
@AnimatedState(
value: false,
animation: .spring()
) var scaleSquare
Now if you change that value it will automatically animate with the given animation.
Here is the final version
struct AnimatedStateExample: View {
@AnimatedState(
value: false,
animation: .spring()
) var scaleSquare
@AnimatedState(
value: false,
animation: .spring().speed(0.5)
) var scaleSquare2
var body: some View {
VStack(spacing: 150) {
Rectangle()
.fill(Color.red)
.frame(width: 100, height: 100)
.scaleEffect(scaleSquare ? 2 : 1)
Rectangle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.scaleEffect(scaleSquare2 ? 2 : 1)
}
.onAppear {
scaleSquare.toggle()
scaleSquare2.toggle()
}
}
}
This eliminate the animation modifier for each square or saves you writing the with animation block every time you need to change state.
Hope this is useful for people.
Top comments (0)