NEW: My new book Pro SwiftUI is out now – level up your SwiftUI skills today! >>

SOLVED: Why isn't background animating

Forums > SwiftUI

@Swink  

Attempting to have a background with a gradient that animates. The background pops up, but the animation does not trigger. Very curious what is happening to cause the animation to not trigger. Thanks in advance for the help!

struct LandingPage: View {

    @State private var animateGradient = false

    @ViewBuilder
    var body: some View {
        if(signedIn){
           RandomView()
        }
        else{
            NavigationView{
                VStack{
                        Image("Image default") // << main image
                            .resizable()
                            .scaledToFit()
                            .frame(width:150, height:150)
                            //.renderingMode(.template)
                            .foregroundColor(.black)
                            .padding(.top, 200)

                        Text("Welcome to Landing Page")
                            .font(.title)
                            .padding()

                    .offset(y:-25) // << adjusts title

                    VStack{
                        NavigationLink(destination:createUserAccount() .navigationBarHidden(true),
                       label:{
                            Text("Get Started").fontWeight(.bold)
                                .frame(minWidth: 0, maxWidth: 200)
                                .padding(10)
                                .foregroundColor(.white)
                            //draw rectange around buttons
                                .background(
                                    RoundedRectangle(cornerRadius: 20)
                                        .fill(
                                            LinearGradient(
                                                colors: [.orange, .yellow],
                                                startPoint: .topLeading,
                                                endPoint: .bottomTrailing
                                            )))
                                        })

                        NavigationLink(destination: userLogin(signUpController: signUpController).navigationBarHidden(true), label: {
                            Text("Login").fontWeight(.semibold)
                                .frame(minWidth:0, maxWidth: 200)
                                .padding(10)
                                .foregroundColor(.black)
                                .overlay( RoundedRectangle(cornerRadius: 25)
                                            .stroke(Color.gray, lineWidth: 3)
                                    )
                        })
                            .padding()
                    }
                    Rectangle()
                        .frame(height: 0)
                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                        .ignoresSafeArea()
                }
                //.background(Color.purple)
                .background(RadialGradient(gradient: Gradient(colors: [.yellow, .green]), center: .center, startRadius: 312, endRadius: animateGradient ? 100 : 450))
                .animation(.linear(duration: 5).repeatForever(autoreverses: true),
                                               value: animateGradient)
                .onAppear {
                    DispatchQueue.main.async {
                            animateGradient.toggle()

                    }

                }
                 .frame(maxWidth: .infinity, maxHeight: .infinity)
                 .ignoresSafeArea()

            }

        }
    }
}

   

@Swink gets animated about a view problem:

Very curious what is happening to cause the animation to not trigger.

Somewhere in these archives, I wrote a small piece about eating an elephant.

See -> Eating an Elephant

How does this relate to your troubles?

You might have too many bites on your plate. It's difficult to chew what you have with so much more in the way.

One Bite at a Time

Clear some things off your plate and think about animating just the background. That's what I did in the code below. I thought about a simple background that toggles from one color to another. The rectangle and globe are just distractions. Comment them out, or delete them. They don't help you solve the problem.

Then work through your design goals. First, you need the view to load itself and initialize all its properties.
Then you want to flip a switch in your application to start the animation.
Then you change the background to the new look with animation. You've told the animation to start with one color and morph into another color.
Then you let the animation know it should reverse itself. And repeat. Forever.

Paste this code into a new project and give it a test drive.

Next, Eat Another Piece

If this works, and you understand the sequence of events, you're ready for the next bite of elephant.
I'd try to change the plain colors to gradients. Maybe change just one at first.

Keep Coding!

Please return and let us know how you solved this!

// Paste into a new Xcode project.
struct BackgroundPulse: View {
    // Think of this as a light switch in your classroom.
    // Once the switch is turned on (changed?) what do you want to happen?
    // Also, take note! This only is changed ONCE in your app.
    // The animation is set to >repeatForever< so no need to turn this on and off!
    @State private var theColor = Color.clear

    // I like computed vars.
    // Keep the view logic nice and tidy.
    var nextBackgroundColor: Color {
        theColor == .indigo ? .green : .indigo  // toggle
    }

    var body: some View {
        ZStack {
            // This content is a distraction. Feel free to mark as comments
            // until you've got the background animations working. 
            // But you do need something to populate the ZStack. πŸ™‚
            Rectangle()
                .fill(Color.pink.opacity(0.8))
                .frame(width: 150, height: 150)
                .cornerRadius(30)
            Image(systemName: "globe")
                .font(.system(size: 90, weight: .thin))
                .foregroundColor(.white)
        }
        // Think of this as turning on the light switch.
        // You're changing the state of your light bulb.
        .onAppear { theColor = .indigo } // kick off the fun!
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .ignoresSafeArea()
        .background(nextBackgroundColor) // Just declare what you want to see!
        // Set the animation rules.
        // 1. Set type and duration.
        // 2. Indicate it repeats forever, and reverses.
        // 3. Wire it up to a light switch. When the value changes, start the animation.
        .animation(.linear(duration: 3.0).repeatForever(autoreverses: true), value: theColor)
    }
}

   

I don't really know enough about it to give you the reason why it isn't animating the way you have it.

But I did find a way to make it work by slightly modifying your code, and it seems to have something to do with using the .background() modifier.

Basically, I just removed the .background() modifier from your VStack (The one just above the .animation() modifier)

Then, I added the gradient as a background by wrapping your entire VStack in a ZStack, and putting the RadialGradient() initializer into the ZStack just before the VStack

Then, move all of the modifiers that were attached to the VStack to the ZStack instead.

Then the background animates.

(I also changed your NavigationLinks to point to Text("Hi") Views here, just so I could get the code to run easily on my end.)

@ViewBuilder var body: some View {
        if(signedIn) {
            Text("Hi")
        } else {
            NavigationView{
                ZStack {
                    RadialGradient(gradient: Gradient(colors: [.yellow, .green]), center: .center, startRadius: 312, endRadius: animateGradient ? 100 : 450)

                    VStack{
                        Image(systemName: "globe") // << main image
                            .resizable()
                            .scaledToFit()
                            .frame(width:150, height:150)
                            .foregroundColor(.black)
                            .padding(.top, 200)

                        Text("Welcome to Landing Page")
                            .font(.title)
                            .padding()
                            .offset(y:-25) // << adjusts title

                        NavigationLink(destination: Text("Hi") .navigationBarHidden(true), label:{
                            Text("Get Started").fontWeight(.bold)
                                .frame(minWidth: 0, maxWidth: 200)
                                .padding(10)
                                .foregroundColor(.white)
                                .background(RoundedRectangle(cornerRadius: 20).fill(
                                    LinearGradient(
                                        colors: [.orange, .yellow],
                                        startPoint: .topLeading,
                                        endPoint: .bottomTrailing
                                    )
                                ))
                        })

                        NavigationLink(destination: Text("hi").navigationBarHidden(true), label: {
                            Text("Login").fontWeight(.semibold)
                                .frame(minWidth:0, maxWidth: 200)
                                .padding(10)
                                .foregroundColor(.black)
                                .overlay( RoundedRectangle(cornerRadius: 25)
                                    .stroke(Color.gray, lineWidth: 3)
                                )
                        })
                        .padding()

                        Rectangle()
                            .frame(height: 0)
                            .frame(maxWidth: .infinity, maxHeight: .infinity)
                            .ignoresSafeArea()
                    }
                }
                .animation(.linear(duration: 5).repeatForever(autoreverses: true), value: animateGradient)
                .onAppear {
                    DispatchQueue.main.async {
                        animateGradient.toggle()
                    }
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .ignoresSafeArea()
            }
        }
    }

   

@Swink  

@Obelix Thank you, thank you, thank you for this. As someone who is self taught and doesn't have any mentors, you have been tremendous in the few questions you helped me on with taking the time, going over the approach in detail and just helping me out. Learned so much from your answers. I owe you a beer (if you don't drink a coke) if we ever cross paths. Thank you again for this

@Fly0strich Thank you for this approach. This helped me figure out what I was doing wrong without rearranging the code too much. I appreciate it !!

   

Hacking with Swift is sponsored by Play

SPONSORED Play is the first native iOS design tool created for designers and engineers. You can install Play for iOS and iPad today and sign up to check out the Beta of our macOS app with SwiftUI code export. We're also hiring engineers!

Click to learn more about Play!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.