UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

interpolatingSpring: change parameter while animation

Forums > SwiftUI

Hi,

I want to change parameter of the animation while the animation is running. But if I change the parameter, the change is ignored until the animation stopped. The goal with this UI:

  • Start animation if user press button 'Animate'
    • blue rectangle moves down and oscillates around the endpoint with lowering amplitude (this works fine)
  • if user press button 'stop' stiffness is changed (this works) and the blue rectangle has to stop oscillation & move slow to the endpoint (this doesnt work)

Whats wrong? What do I need to change change to reach the goal?

thank you for your ideas & help.

Here is the source:

import SwiftUI

struct ContentView: View {
    @State private var isAnimating = false
    let positionStart = CGPoint(x: 0, y: -300)
    let positionEnd = CGPoint(x: 0, y: 0)
    @State private var animationMass = 3.0
    @State private var animationStiff = 150.0
    @State private var animationDamp = 5.0

    var body: some View {
        ZStack {
            Rectangle()
                .fill(Color.blue)
                .frame(width: 200, height: 100)
                .cornerRadius(20)
                .offset(y: isAnimating ? positionEnd.y : positionStart.y)

            VStack{
                HStack{
                    Button(action: {
                        withAnimation(.interpolatingSpring(mass: animationMass, stiffness: animationStiff, damping: animationDamp)) {
                            self.isAnimating = true
                        }
                    }) {
                        Text("Animate")
                    }
                    .frame(width: 100, height: 50)
                    .background(Color.yellow)
                    .cornerRadius(15)

                    Button(action: {
                        withAnimation {
                            animationDamp = 100
                        }
                    }) {
                        Text("stop")
                    }
                    .frame(width: 100, height: 50)
                    .background(Color.yellow)
                    .cornerRadius(15)
                }
                HStack{
                    Text("Mass \(animationMass, specifier: "%.1f")")
                        .padding(.horizontal)
                    Text("1")
                    Slider(value: $animationMass,  in: 1...10, step: 0.5)
                    Text("10")
                        .padding(.trailing)
                }
                HStack{
                    Text("Stiffness \(animationStiff, specifier: "%.0f")")
                        .padding(.horizontal)
                    Text("0")
                    Slider(value: $animationStiff,  in: 0...200, step: 1)
                    Text("200")
                        .padding(.trailing)
                }
                HStack{
                    Text("Damping \(animationDamp, specifier: "%.0f")")
                        .padding(.horizontal)
                    Text("0")
                    Slider(value: $animationDamp,  in: 0...100, step: 1)
                    Text("100")
                        .padding(.trailing)
                }
            }
            .offset(y: 200)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

1      

Hi, If you want the square to move back to the starting position you need to change the offset again, so in the stop button action you need to set isAnimatting to false.

1      

Hi, I dont want it back tothe start point. This is not the problem. I want the blue rectangle to reach the endpoint immediately after clicking 'stop'. If you dont click 'stop' it needs 5 sec to reach the endpoint. Thats okay. But if I click 'stop' one or two seconds after the start, it has to reach the endpoint immediately. The oscillation has to stop.

1      

If you read the documentation for .interpolatingSpring it states that the velocity is preserved across overlapping animations by adding the effects of each animation. What this means is that it will combine animations.

I have made some modifications to your code to help illustrate this.

You want Animation 2 to happen but from wherever the box happens to be at the time you press the button. However, if Animation 1 is already going, the animations are combined. Even stopping Animation 1 by setting its isAnimating parameter to false does not help.

I don't think that it is possible achieve what you want using .interpolatingSpring.

Add these changes to illustrate the animations.

New @State variable

@State private var isAnimating2 = false

Change to the Rectangle

Rectangle()
    .fill(Color.blue)
    .frame(width: 200, height: 100)
    .cornerRadius(20)
    .offset(y: isAnimating ? positionEnd.y :
               isAnimating2 ? positionEnd.y : positionStart.y)   // modifed

Changes to he HStack Buttons

HStack{
    Button(action: {
        withAnimation(.interpolatingSpring(mass: animationMass, stiffness: animationStiff, damping: animationDamp)) {
            self.isAnimating = true
            self.isAnimating2 = false   // added
        }
    }) {
        Text("Animation 1")    // modified
    }
    .frame(width: 100, height: 50)
    .background(Color.yellow)
    .cornerRadius(15)

    Button(action: {
            withAnimation(.interpolatingSpring(mass: animationMass, stiffness: animationStiff, damping: 100)) {    // added
            animationDamp = 100
            self.isAnimating = false    // added
            self.isAnimating2 = true   // added
        }
    }) {
        Text("Animation 2")    // modified
    }
    .frame(width: 100, height: 50)
    .background(Color.yellow)
    .cornerRadius(15)

    // New button added
    Button(action: {
        withAnimation {
            animationDamp = 5
            self.isAnimating = false
            self.isAnimating2 = false
        }
    }) {
        Text("Reset")
    }
    .frame(width: 100, height: 50)
    .background(Color.mint)
    .cornerRadius(15)
}

1      

Hi,

youre right, there is no official way to do this. I used a trick to get it solved: Manual fire the trigger for the next animation in the event of button-press. Thanks for the help. Ciao

1      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

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

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

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.