In the code below, if I change the part:
Image(systemName: "trash.fill")
.opacity(state == .active ? opacity : 0.0001)
with:
Image(systemName: "trash.fill")
.opacity(state == .active ? opacity : 0.0)
The transition does not complete. There's a series of animations that happen and at the end the image of the trashcan is supposed to fade (opacity), with a value of 0.0 this never happens, the trashcan just disappears immediately.
I could explain more but its better if you run the code with both versions of that part, first with 0.0001 (or any value really) and then with 0.0 to see what I mean
enum TransitionState{
case active
case identity
}
struct TrashCanTransition: ViewModifier{
let state: TransitionState
@State private var scaleEffect = 1.0
@State private var offsetY = 0.0
@State private var contentOpacity = 1.0
@State private var opacity = 0.0
func body(content: Content) -> some View {
GeometryReader{geo in
let frame = geo.frame(in: .local)
content
.position(x: geo.size.width / 2, y: geo.size.height / 2)
.scaleEffect(state == .active ? scaleEffect : 1.0)
.offset(x: 0, y: state == .active ? offsetY : 0)
.opacity(state == .active ? contentOpacity : 1)
.onChange(of: state){current in
if current == .active{
withAnimation(.linear(duration: 1)){
scaleEffect = 0.3
offsetY = -(geo.size.height / 2) + 10
}
}
}
.onChange(of: contentOpacity){_ in
withAnimation(.linear.delay(3)){
opacity = 0.0
}
}
Image(systemName: "trash.fill")
.opacity(state == .active ? opacity : 0.0001)
.foregroundColor(.red)
.font(.system(size: 40))
.position(x: frame.midX, y: frame.maxY + 10)
.onChange(of: scaleEffect){_ in
withAnimation(.linear(duration: 1).delay(1)){
opacity = 1.0
}
}
.onChange(of: opacity){value in
withAnimation(.linear(duration: 1).delay(2)){
offsetY = (geo.size.height / 2) + 10
contentOpacity = 0.0
}
}
}
}
}
extension View{
func withTrashCanTransition()-> some View{
self
.transition(
.modifier(active: TrashCanTransition(state: .active), identity: TrashCanTransition(state: .identity))
)
}
}
struct SimpleView: View{
@State private var showCircle = false
var body: some View{
VStack{
if showCircle{
Circle()
.fill(.red)
.frame(
width: 200,
height: 200
)
.withTrashCanTransition()
}
Spacer()
Toggle("Show circle", isOn: $showCircle.animation(.linear(duration: 1)))
}
.padding()
}
}
I don't get what's happening, I think that maybe I'm not using .modifier(active:identity:) correctly, or maybe is a problem with identity (like swiftUI is considering some states as different views) but I honestly have no idea, it might be something else.