Search code examples
swiftswiftuiswiftui-listswiftui-animation

Animated a listRowBackground gradient?


I have a view that looks like this: enter image description here

I would like to animate the gradient in the listRowBackground, but I am not able to for some reason. Here is the code I have:

import SwiftUI

struct SnakeColorView: View {
    @AppStorage("HeadColor") private var HeadColor : Color = .white
    @AppStorage("BodyColor") private var BodyColor : Color = .white
    
    @State private var animateGradient : Bool = false
    
    var body: some View {
        let bgGradient = LinearGradient(colors: [Color("Bi Pink"), Color("Bi Purple"), Color("Bi Blue")], startPoint: animateGradient ? .top : .bottom, endPoint: animateGradient ? .bottom : .top)
        
        List {
            ColorPicker("Set the color of the Snake's head!!", selection: $HeadColor)
            ColorPicker("Set the color of the Snake's body!!", selection: $BodyColor)
            HStack{
                Spacer()
                VStack(spacing: 0) {
                    ZStack{
                        Ellipse()
                            .fill(HeadColor)
                        Ellipse()
                            .strokeBorder(.white, lineWidth: 3.2)
                        
                    }.frame(width: 100, height: 175)
                    ForEach(0..<7) { _ in
                        ZStack {
                            Circle()
                                .fill(BodyColor)
                            Circle()
                                .strokeBorder(.white, lineWidth: 3.2)
                        }
                    }.frame(width: 50, height: 50)
                }
                Spacer()
            }
            .listRowBackground(bgGradient)
            .ignoresSafeArea()
            .onAppear {
                withAnimation(.linear(duration: 2.0).repeatForever(autoreverses: true)) {
                    animateGradient.toggle()
                }
            }
        }
    }
}

I have the state variable for the animation, and it should be triggered by onAppear but nothing happens.


Solution

  • Add .animation property with value to explicitly telling LinearGradient what animation to use when animateGradient value changes. The following code with output:

    Code:

    @State private var animateGradient : Bool = false
        
        var body: some View {
            let bgGradient = LinearGradient(colors: [Color.pink, Color.purple, Color.blue], startPoint: animateGradient ? .topLeading : .bottomTrailing, endPoint: animateGradient ? .bottomTrailing : .topLeading)
            
            List {
                HStack{
                    Spacer()
                    VStack(spacing: 0) {
                        ZStack{
                            Ellipse()
                                .fill(Color.red)
                            Ellipse()
                                .strokeBorder(.white, lineWidth: 3.2)
                            
                        }.frame(width: 100, height: 175)
                        ForEach(0..<7) { _ in
                            ZStack {
                                Circle()
                                    .fill(Color.yellow)
                                Circle()
                                    .strokeBorder(.white, lineWidth: 3.2)
                            }
                        }.frame(width: 50, height: 50)
                    }
                    Spacer()
                }
                .listRowBackground(
                    bgGradient
                        .animation(.linear(duration: 5.0).repeatForever(autoreverses: true), value: animateGradient) // ---> add animation
    
                )
                .ignoresSafeArea()
                .onAppear {
                    animateGradient.toggle() // --> toggle value
                    
                }
            }
        }
    

    Output:

    enter image description here