How can I create a smooth colour change animation using swiftUI? (Example in question)

Viewed 462

I have a play/pause button that changes when pressed. At the moment it just fades in and out but I would like it to perform a animation that looks like the new colour is flowing over the old one. enter image description here

enter image description here

enter image description here

enter image description here

This is what I have so far:

if(meditationViewModel.timerIsRunning){
    HStack{
        Image(systemName: "pause")
        Text("STOP")
            .bold()
     }
      .padding()
      .foregroundColor(.white)
      .background(Color.red)
      .cornerRadius(25)
      .shadow(radius: 20)
}else{
    HStack{
        Image(systemName: "play")
        Text("PLAY")
            .bold()
    }
     .padding()
     .foregroundColor(.white)
     .background(Color.green)
     .cornerRadius(25)
}

The change of meditationViewModel.timerIsRunning happens elsewhere.

1 Answers

Here is a demo of possible approach - just use some shape (circle in this case) between text and opaque background and move it depending on state.

Prepared with Xcode 12.4 / iOS 14.4

Note: you can use just tap gesture instead of button and tune all parameters and colors, but in general the idea remains the same

demo

struct DemoView: View {
    @State private var playing = false
    var body: some View {
        Button(action: { playing.toggle() }) {
            HStack{
                Image(systemName: playing ? "pause" : "play")
                Text(playing ? "STOP" : "PLAY")
                    .bold()
            }
            .background(
                Circle().fill(Color.purple)
                    .frame(width: 160, height: 160, alignment: .leading)
                    .blur(radius: 3)
                    .offset(x: playing ? 0 : -160, y: -40)
                    .animation(.easeInOut(duration: 1), value: playing)
            )
            .padding()
            .foregroundColor(.white)
            .background(Color.green)
            .cornerRadius(25)
            .clipped()
            .shadow(radius: 20)
        }
    }
}
Related