To get your animation begin once more, you want to reset part
worth earlier than begin animation. After WaveView
seem, part
worth get set to .pi * 2
, then whenever you press button, you simply set it to the identical worth, SwiftUI don’t see any change of part
so your animation shouldn’t be run, additionally when onChange
closure of isAnimating
get known as, a brand new withAnimation
block get known as subsequently present animation repeatForever
get cancel as a result of animation
for part
and energy
are stick collectively in AnimatablePair
:
personal func animateWave() {
part = 0 // <- reset part worth right here
withAnimation(Animation.linear(length: 2).repeatForever(autoreverses: false)) {
self.part = .pi * 2
}
}
You possibly can remark out withAnimation
block in onChange(of: isAnimating)
to maintain repeatForever
animation, however then you’ll not have animation when waveStrength
change:
.onChange(of: isAnimating) { _, newValue in
//withAnimation(.easeInOut(length: 0.5)) {
waveStrength = newValue ? 50.0 : 0.0
//}
if newValue {
animateWave()
}
}
One other repair is to seperate part
and waveStrength
from AnimatablePair
:
struct ShapheWave: View, @preconcurrency Animatable {
var energy: Double
var frequency: Double
var part: Double
var animatableData: Double {
get { part }
set {
self.part = newValue
}
}
var physique: some View {
Wave(energy: energy, frequency: frequency, part: part)
.stroke(.black, lineWidth: 5)
}
}
struct Wave: Form {
var energy: Double
var frequency: Double
var part: Double = .pi * 2
var animatableData: Double {
get { energy }
set {
self.energy = newValue
}
}
func path(in rect: CGRect) -> Path {
var path = Path()
let width = Double(rect.width)
let peak = Double(rect.peak)
let midHeight = peak / 2
let wavelength = width / frequency
let firstX = 0.0
let firstRelativeX = firstX / wavelength
let firstSine = sin(firstRelativeX + part)
let firstY = energy * firstSine + midHeight
path.transfer(to: CGPoint(x: firstX, y: firstY))
for x in stride(from: 0.0, via: width, by: 1) {
let relativeX = x / wavelength
let sine = sin(relativeX + part)
let y = energy * sine + midHeight
path.addLine(to: CGPoint(x: x, y: y))
}
return path
}
}
struct WaveView: View {
@Binding var isAnimating: Bool
@State personal var part = 0.0
@State personal var waveStrength = 50.0
var physique: some View {
VStack {
ShapheWave(energy: waveStrength, frequency: 30, part: part)
.onChange(of: isAnimating) { _, newValue in
waveStrength = newValue ? 50.0 : 0.0
}
.onAppear {
animateWave()
}
.animation(.linear(length: 2).repeatForever(autoreverses: false), worth: part) .animation(.easeInOut(length: 0.5), worth: waveStrength)
}
}
personal func animateWave() {
part = .pi * 2
}
}