I have three properties. Two with different animations on change and one with no animation.
struct Stick: View {
var color: Color
var body: some View {
GeometryReader { global in
Path { path in
path.move(to: CGPoint(x: global.size.width/2, y: 0))
path.addLine(to: CGPoint(x:global.size.width/2,y: global.size.height))
}
.stroke(color,lineWidth: 200)
}
}
}
struct SuperStick: View {
@Binding var progress: Float
@State var offset: CGFloat = 0
@State var color: Color = .blue.opacity(0.5)
var body: some View {
GeometryReader { global in
ZStack(alignment: .bottom) {
Stick(color: color)
.onAppear {
if offset >= 1 {
offset = 0
}
offset += 1
color = .green.opacity(0.5)
}
}
.frame(width: global.size.width, height: global.size.height)
// Animation 1
// .animation(.linear(duration: 0.1).repeatForever(autoreverses: true), value: color)
// Animation 2
.position(x: global.size.width * (0.3+0.4*offset),y: global.size.height/2)
.animation(.linear(duration: 1).repeatForever(autoreverses: false), value: offset)
// Animation 3
.offset(y:CGFloat(1-progress)*global.size.height)
.animation(.easeInOut(duration: 0.4), value: progress)
}
}
}
struct TestView: View {
@State var progress: Float = 0.6
var body: some View {
ZStack{
SuperStick(progress: $progress)
.edgesIgnoringSafeArea(.all)
VStack {
Stepper("progress: \(String(format: "%.1f", progress))",value: $progress, in: 0.0...1.0, step: 0.2)
.padding(.horizontal,40)
}
}
}
}
#Preview {
TestView()
}
We can see that Animation1 is commented, so there should only be animations for offset and progress. However, we can see that color gets animated as well, with the same speed as offset.
Then I attempted to add an animation for color, hoping color to have its own animation (just uncomment the line below Animation1), however, this time, offset has the same animation as color instead of it's own Animation2.
And then the weirdest thing happened.
If you comment Animation1 but keep Animation2, and then try to change progress, you'll see that although we only have Animation2 and Animation3, and color is just somehow affected by offset to have Animation2,
when you change progress, Animation2 on offset stops (interrupted by Animation3) but color keeps changing...

Why is everything happening?


You are having those issues because when you, for example, change the progress using the Stepper, the View gets re-rendered, but since the View was already being displayed and the animtions were initialised in the onAppear they did not get a proper reinitialisation. I'm talking about the offset animation specifically. Also, the animation modifier documentation says this:
So, what happens is that when you use something like this:
.animation(.linear(duration: 1).delay(0.1).repeatForever(autoreverses: true), value: color)is that when color changes it animates the entire view to which the animation modifier is applied to, not only the color value. To only animate the color value you should use the withAnimation function, which animates any part of the code that uses the color property.I've modified your code like so:
I've used the iOS 17 onChange modifier, but you can use the old one too, you only need to pass one parameter in the closure instead of two. In the onChange I'm reinitialising the offset animation so that when a re-render happens based on progress value change the animation refreshes too. I hope to have been clear with this explaination and not to have commited mistakes. Let me know your thoughts and if this code works for you!