1

Is there a way to change the reverse animation speed, so that it reverses quickly and then goes back to the original speed? I'm using an animation like this.

static private let backgroundAnimation = Animation
        .linear(duration: time)
        .delay(1.0)
        .repeatForever(autoreverses: true)

Thanks!

rmaddy
  • 314,917
  • 42
  • 532
  • 579
SpringN
  • 904
  • 1
  • 11
  • 21

1 Answers1

0

If SwiftUI doesn't provide an option to speed up the reverse animation, then a way to achieve it is by applying your own Animatable view modifier. You need to disable the built-in reverse animation by applying .repeatForever(autoreverses: false). Then implement the reverse part of the animation as the tail end of the "forward" animation.

Here's an example:

struct OffsetModifier: ViewModifier, Animatable {

    private let maxOffset: CGFloat
    private let rewindSpeedFactor: Int
    private var progress: CGFloat

    // The progress value at which rewind begins
    private let rewindThreshold: CGFloat

    init(
        maxOffset: CGFloat,
        rewindSpeedFactor: Int = 4,
        progress: CGFloat
    ) {
        self.maxOffset = maxOffset
        self.rewindSpeedFactor = rewindSpeedFactor
        self.progress = progress

        // Compute the threshold at which rewind begins
        self.rewindThreshold = CGFloat(1) - (CGFloat(1) / CGFloat(rewindSpeedFactor + 1))
    }

    /// Implementation of protocol property
    var animatableData: CGFloat {
        get { progress }
        set { progress = newValue }
    }

    var xOffset: CGFloat {
        let fraction: CGFloat
        if progress > rewindThreshold {
            fraction = rewindThreshold - ((progress - rewindThreshold) * CGFloat(rewindSpeedFactor))
        } else {
            fraction = progress
        }
        return (fraction / rewindThreshold) * maxOffset
    }

    func body(content: Content) -> some View {
        content.offset(x: xOffset)
    }
}

struct ContentView: View {

    private let diameter = CGFloat(30)
    @State private var progress = CGFloat.zero

    var body: some View {
        VStack {
            GeometryReader { proxy in
                Circle()
                    .fill()
                    .foregroundColor(.red)
                    .frame(width: diameter, height: diameter)
                    .modifier(OffsetModifier(maxOffset: proxy.size.width - diameter, progress: progress))
                    .animation(.linear(duration: 3.0).repeatForever(autoreverses: false), value: progress)
            }
            .frame(height: diameter)
            .onAppear { progress = 1.0 }
        }
    }
}

enter image description here

Benzy Neez
  • 1,546
  • 2
  • 3
  • 10