I have been testing how transitions work based on SwiftUI-labs but I found that transitions should not all be implemented in the same way. Based on the previous article:
Note that since XCode 11.2, transitions no longer work with implicit animations
Thing is, that I found something strange. Some transitions work fine when using either implicit animation OR by associating an animation to it. So, what animation should be used? Well, it depends. On what? I don't know. I'm hoping anyone can help me explaining all this.
In the following test, I have created 5 views, each one associated with a different transition: .opacity
,.scale
, .move
, .slide
, and combined
(.opacity
and .move
). Here are my findings:
NOTE: I'M USING XCODE 11.4 AND THE SIMULATOR!
Transitions using implicit animation
Only .move
and .slide
transitions work OK (removal and insertion).
Transitions using explicit animation
Removal and insertion animation works OK for all transitions.
Associating an animation to each transition
Only .scale
and .opacity
transitions work OK (removal and insertion).
From my point of view, not having a standard way of implementing transitions complicates things, moreover when combining transitions (.combined
).
Am I missing something about the implementation? Here's my code:
struct TestAnimation: View {
@State var show : Bool = true
var colors : [Color] = [.orange, .yellow, .green, .blue, .pink]
var body: some View {
VStack(alignment: .leading) {
Spacer()
Color.purple
.frame(height: 100)
.overlay(
Text("Tap Me!").foregroundColor(.white))
.onTapGesture {
// (#1) implicit animation
self.show.toggle()
// (#2) explicit animation
/*withAnimation(Animation.easeInOut(duration: 1)) {
self.show.toggle()
}*/
// (#3) associate an animation with a transition
//self.show.toggle()
}
HStack {
if show {
Rectangle()
.fill(colors[0])
.frame(width: 70, height: 100)
.overlay(Text("opacity"))
.transition(AnyTransition.opacity) // (#1) - doesn't animate, only removes/inserts the view
.animation(.easeInOut(duration: 1)) // (#1)
//.transition(AnyTransition.opacity) // (#2) - only animates the removal, not the insertion
//.transition(AnyTransition.opacity.animation(.easeInOut(duration: 1))) // (#3) - animates removal and insertion
Rectangle()
.fill(colors[1])
.frame(width: 70, height: 100)
.overlay(Text("scale"))
.transition(AnyTransition.scale) // (#1) - doesn't animate, only removes/inserts the view
.animation(.easeInOut(duration: 1)) // (#1)
//.transition(AnyTransition.scale) // (#2) - only animates the removal, not the insertion
//.transition(AnyTransition.scale.animation(.easeInOut(duration: 1))) // (#3) - animates removal and insertion
Rectangle()
.fill(colors[2])
.frame(width: 70, height: 100)
.overlay(Text("move"))
.transition(AnyTransition.move(edge: .bottom)) // (#1) - animates removal and insertion
.animation(.easeInOut(duration: 1)) // (#1)
//.transition(AnyTransition.move(edge: .bottom)) // (#2) - only animates the removal, not the insertion
//.transition(AnyTransition.move(edge: .bottom).animation(.easeInOut(duration: 1))) // (#3) - doesn't animate, only removes/inserts the view
Rectangle()
.fill(colors[3])
.frame(width: 70, height: 100)
.overlay(Text("slide"))
.transition(AnyTransition.slide) // (#1) - animates removal and insertion
.animation(.easeInOut(duration: 1)) // (#1)
//.transition(AnyTransition.slide) // (#2) - only animates the removal, not the insertion
//.transition(AnyTransition.slide.animation(.easeInOut(duration: 1))) // (#3) - doesn't animate, only removes/inserts the view
Rectangle()
.fill(colors[4])
.frame(width: 70, height: 100)
.overlay(Text("op&mv"))
.transition(AnyTransition.opacity.combined(with: .move(edge: .bottom))) // (#1) - doesn't animate, only removes/inserts the view
.animation(.easeInOut(duration: 1)) // (#1)
//.transition(AnyTransition.opacity.combined(with: .move(edge: .bottom))) // (#2) - only animates removal, not the insertion
//.transition(AnyTransition.opacity.combined(with: .move(edge: .bottom)).animation(.easeInOut(duration: 1))) // (#3) - animates removal (it's not smooth), and only animates opacity on insertion
}
}
.frame(height: 100)
.padding(7)
.border(Color.gray)
}
}
}