Is there an equivalent of FastOutSlowInInterpolator for iOS?

Viewed 289

Is there an equivalent of FastOutSlowInInterpolator for iOS? I've recently got my hands on AndroidX and really like this interpolator. I've found the source code for it too but have no idea how to convert it to iOS implementation.

2 Answers

If you are using UIViewPropertyAnimator the curve that you need is .easeInOut, and you can pass it as curve parameter when you create your animator:

let animator = UIViewPropertyAnimator(duration: 0.4, curve: .easeInOut) {
    // Animations
}

If you are not happy with this system curve, you can follow this answer and use this handy website to replicate FastOutSlowInInterpolator's control points.

As FastOutSlowInInterpolator documentation states:

Interpolator corresponding to fast_out_slow_in. Uses a lookup table for the Bezier curve from (0,0) to (1,1) with control points: P0 (0, 0) P1 (0.4, 0) P2 (0.2, 1.0) P3 (1.0, 1.0)

So, in your particular case you are looking for something like this:

let timingParameters = UICubicTimingParameters(
    controlPoint1: CGPoint(x: 0.4, y: 0),
    controlPoint2: CGPoint(x: 0.2, y: 1)
)
let animator = UIViewPropertyAnimator(duration: 0.4, timingParameters: timingParameters)

or this:

let animator = UIViewPropertyAnimator(
    duration: 0.4,
    controlPoint1: CGPoint(x: 0.4, y: 0),
    controlPoint2: CGPoint(x: 0.2, y: 1)
) {
    // Animations
}

SwiftUI

In SwiftUI you can animate almost any change with .animate modifier and it accepts the curve as the argument. I think .interpolatingSpring(stiffness: 30, damping: 20) is the curve that you are looking for (the bottom one).

Examples

Demo

struct ContentView: View {
    @State var isLeading = false

    var body: some View {
        VStack {
            SpacedCircle(isLeading: $isLeading)
                .animation(.linear)

            SpacedCircle(isLeading: $isLeading)
                .animation(.easeIn)

            SpacedCircle(isLeading: $isLeading)
                .animation(.easeOut)

            SpacedCircle(isLeading: $isLeading)
                .animation(.easeInOut)

            SpacedCircle(isLeading: $isLeading)
                .animation(.interactiveSpring(response: 0.27, dampingFraction: 0.5, blendDuration: 0.2))

            SpacedCircle(isLeading: $isLeading)
                .animation(.interpolatingSpring(stiffness: 30, damping: 20))

            Button("Toggle") { isLeading.toggle() }
        }
    }
}

UIKit

You can achieve that similarly in UIKit too:

Example

UIKit Demo

    @IBAction func touched(_ sender: UIButton) {
        UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 3, options: []) {
            self.circleView.transform = .identity
        }
    }

    @IBAction func touchDown(_ sender: Any) {
        UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 3, options: []) {
            self.circleView.transform = CGAffineTransform(translationX: 240, y: 0)
        }
    }
Related