Combined image change and movement animation doesn't play properly

47 views Asked by At

Having simple code like below, the animation of changing the image and movement is not displayed properly. The image is not moving together with the whole view, but fades out and then fades in in the destination spot. Please see the image.

struct AnimationTestView: View {
    @State var selected: Bool = false
    
    var body: some View {
        HStack {
            if !selected {
                Image(systemName: "plus.circle")
                    .resizable()
                    .frame(width: 50, height: 50)
                    .background(Color.green)
            }
            
            Image(systemName: selected ? "plus.circle.fill" : "plus.circle")
                .resizable()
                .frame(width: 50, height: 50)
                .background(Color.yellow)
                .onTapGesture {
                    withAnimation {
                        selected.toggle()
                    }
                }
            Spacer()
        }
    }
}

enter image description here

Edit: Found a solution, inspired by Benzy Neez's answer. What makes it working is simply adding .drawingGroup() modifier to the second image.

1

There are 1 answers

4
Benzy Neez On

When you change the name of the image, this actually replaces the image, so a transition happens. This is why you are seeing one image fade out and the other fade in.

The way to avoid this is to show the filled image in an overlay. The image is always there, but you change its opacity according to the selection. You said in a comment that you wanted the transition to happen at the destination point, so you can add a delay to the opacity change to achieve this. However, in order that the two images slide over to the other position together, the overlay and the base need to be combined by applying .drawingGroup. Like this:

HStack {
    if !selected {
        Image(systemName: "plus.circle")
            .resizable()
            .frame(width: 50, height: 50)
            .background(Color.green)
    }
    Image(systemName: "plus.circle")
        .resizable()
        .frame(width: 50, height: 50)
        .background(Color.yellow)
        .overlay {
            Image(systemName: "plus.circle.fill")
                .resizable()
                .background(.yellow)
                .opacity(selected ? 1 : 0)
                .animation(.easeInOut.delay(selected ? 0.2 : 0), value: selected)
        }
        .drawingGroup()
        .onTapGesture {
            withAnimation {
                selected.toggle()
            }
        }
    Spacer()
}

Animation