The idea is this: an object has a start position (A) and an end position (B). When the start button is pressed, the object moves from Ax, Ay to Bx, By. At an arbitrary point in time, you can stop its movement and continue.
I tried to implement this with a timer that moves the object every n time periods. It works, but the problem is that the timer eats up a lot of memory and when ten objects are created, the application freezes.
I do not ask you to do the task for me, but I will be very grateful if you tell me where to dig
Code currently in use
typealias RemainingDurationProvider<Value: VectorArithmetic> = (Value) -> TimeInterval
typealias AnimationWithDurationProvider = (TimeInterval) -> Animation
extension Animation {
static let instant = Animation.linear(duration: 0.0001)
}
struct PausableAnimationModifier<Value: VectorArithmetic>: AnimatableModifier {
@Binding var binding: Value
@Binding var paused: Bool
private let targetValue: Value
private let remainingDuration: RemainingDurationProvider<Value>
private let animation: AnimationWithDurationProvider
var animatableData: Value
init(binding: Binding<Value>, targetValue: Value, remainingDuration: @escaping RemainingDurationProvider<Value>, animation: @escaping AnimationWithDurationProvider, paused: Binding<Bool>) {
_binding = binding
self.targetValue = targetValue
self.remainingDuration = remainingDuration
self.animation = animation
_paused = paused
animatableData = binding.wrappedValue
}
func body(content: Content) -> some View {
content
.onChange(of: paused) { isPaused in
if isPaused {
withAnimation(.instant) {
binding = animatableData
}
} else {
withAnimation(animation(remainingDuration(animatableData))) {
binding = targetValue
}
}
}
}
}
extension View {
func pausableAnimation<Value: VectorArithmetic>(binding: Binding<Value>, targetValue: Value, remainingDuration: @escaping RemainingDurationProvider<Value>, animation: @escaping AnimationWithDurationProvider, paused: Binding<Bool>) -> some View {
self.modifier(PausableAnimationModifier(binding: binding, targetValue: targetValue, remainingDuration: remainingDuration, animation: animation, paused: paused))
}
}
struct TestView: View {
@State private var isPaused = false
@State private var offsetX: Double = .zero
@State private var startOffsetX: Double = .zero
@State private var endOffsetX: Double = 200.0
@State private var offsetY: Double = .zero
@State private var startPositionY: Double = .zero
@State private var endPositionY: Double = 500.0
private let duration: TimeInterval = 6
private var remainingDurationForX: RemainingDurationProvider<Double> {
{ currentAngle in duration * (1 - (currentAngle - startOffsetX) / (endOffsetX - startOffsetX)) }
}
private var remainingDurationForY: RemainingDurationProvider<Double> {
{ currentAngle in duration * (1 - (currentAngle - startPositionY) / (endPositionY - startPositionY)) }
}
private let animation: AnimationWithDurationProvider = { duration in
.linear(duration: duration)
}
var body: some View {
VStack {
ZStack {
VStack {
HStack {
Rectangle()
.frame(width: 50, height: 50)
.offset(x: offsetX, y: offsetY)
.pausableAnimation(binding: $offsetX,
targetValue: endOffsetX,
remainingDuration: remainingDurationForX,
animation: animation,
paused: $isPaused)
.pausableAnimation(binding: $offsetY,
targetValue: endPositionY,
remainingDuration: remainingDurationForY,
animation: animation,
paused: $isPaused)
Spacer()
}
Spacer()
}
}
HStack {
ControllButton(text: “Start”, action: {
offsetX = startOffsetX
offsetY = startPositionY
withAnimation(animation(duration)) {
offsetX = endOffsetX
offsetY = endPositionY
}
})
ControllButton(text: “NewPosition”, action: {
startOffsetX = endOffsetX
startPositionY = endPositionY
endOffsetX = 300
endPositionY = 30
})
ControllButton(text: isPaused ? “Resume” : “Pause”, action: {
isPaused = !isPaused
})
ControllButton(text: “Stop”, action: {
offsetX = .zero
offsetY = .zero
})
}
.padding(.bottom)
}
}
}
struct ControllButton: View {
var text: String
var action: () -> ()
var body: some View {
Button(text) {
action()
}
.padding()
.background(Color.yellow)
.frame(height: 35)
.cornerRadius(10)
}
}