0

I am trying to animate a gradient change but it keeps happening instantly rather than animating. My current code is as follows

struct GradientView: View {
    
@State var currentTopColor = Color.white

var body: some View {
    LinearGradient(gradient: Gradient(colors: [currentTopColor, .white]), startPoint: .top, endPoint: .bottom)
        .onTapGesture {
            withAnimation(.easeInOut(duration: 1)) {
                currentTopColor = [Color.blue, Color.pink, Color.red].randomElement()!
            }
        }
}

}

I have also tried using the .animation modifier but the effect is the same. When I tap the background it changes instantly rather than gradually.

Can you advise why its happening instantly and how to make the gradient change be gradual with an animation?

Update Latest attempt to use bool for animation still causes instant color change @State private var animate = false

var body: some View {
    ZStack {
        RoundedRectangle(cornerRadius: 20)
            .fill(LinearGradient(gradient: Gradient(colors: [getColor(), .white]), startPoint: .top, endPoint: .bottom))
            .animation(Animation.easeInOut.speed(1.25), value: animate)
    }
    .frame(width: 300, height: 300)
    .onTapGesture {
        animate.toggle()
    }
}

func getColor() -> Color {
    animate ? Color.red : Color.blue
}
Aggressor
  • 13,323
  • 24
  • 103
  • 182
  • Instead of setting a `Color`, you should set a `Bool` and then derive the `Color`s from that `Bool` state. – jnpdx Apr 24 '23 at 22:19
  • Can you clarify? Im not clear what you mean by derive colors from a bool state – Aggressor Apr 24 '23 at 22:33
  • `pressed ? Color.white : Color.green` – jnpdx Apr 24 '23 at 22:52
  • I changed the code and the transition still happens instantly ``` .onTapGesture { myBool = !myBool withAnimation(.easeInOut(duration: 10)) { // I also put the bool change here currentTopColor = myBool ? Color.red : Color.blue } } ``` – Aggressor Apr 24 '23 at 22:58
  • Looks like I also needed to add `.animation(Animation.easeInOut.speed(0.25), value: animate)` – Aggressor Apr 24 '23 at 23:04
  • Correction, the animation only worked when I used it with a color instead of gradient. Ive updated my code with the latest (it still hard transitions) – Aggressor Apr 24 '23 at 23:14
  • Animating between two different `Gradient`s is not supported out of the box (like stop points), but it is definitely achievable by utilizing `AnimatableModifier`. It is quite involving, so instead of compiling an answer I can point you to [this article](https://www.appcoda.com/animate-gradient-swiftui/) which discusses all options. – Alladinian Apr 25 '23 at 08:06
  • Yes I was also looking at that – Aggressor Apr 25 '23 at 15:19
  • The [iOS & iPadOS 17 Beta 2 Release Notes](https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-17-release-notes) state that "Changing from one Gradient value to another will now animate, but only once apps are rebuilt against the new SDK. (107408691)". – Benzy Neez Jul 03 '23 at 19:38

1 Answers1

0

I am guessing that a switch of ShapeStyle is not animatable, I couldn't get it to work either. As a workaround, you can use a transition between views in a ZStack:

struct GradientView: View {

    @State var withBlueGradient = false

    private var filledArea: some View {
        RoundedRectangle(cornerRadius: 20).fill(
            LinearGradient(
                gradient: Gradient(colors: [withBlueGradient ? .blue : .red, .white]),
                startPoint: .top,
                endPoint: .bottom
            )
        )
    }

    var body: some View {
        ZStack {
            if withBlueGradient {
                filledArea
            } else {
                filledArea
            }
        }
        .frame(width: 300, height: 300)
        .onTapGesture {
            withAnimation {
                withBlueGradient.toggle()
            }
        }
    }
}
Benzy Neez
  • 1,546
  • 2
  • 3
  • 10
  • Interesting suggestion Ill play around with this! – Aggressor Apr 25 '23 at 15:21
  • Even if you are switching between lots of different colors, you still only need one ```if...else``` in the ```ZStack```. You just need to make sure that it flip-flops between views when the color changes. A container that combines a color with a counter could be used to achieve this. – Benzy Neez Apr 26 '23 at 10:41