Hashem Aboonajmi
Hashem Aboonajmi

Reputation: 13870

add UIPanGestureRecognizer velocity to UIViewPropertyAnimator

I have followed a tutorial on how to create interactive iOS control center animation using UIViewPropertyAnimator:

http://www.swiftkickmobile.com/building-better-app-animations-swift-uiviewpropertyanimator/

when swiping up or down the bottom menu, after releasing the finger, I want to add pan velocity to UIViewPropertyAnimator and continue the animation:

popupViewPanned(recognizer:) {

    switch recognizer.state {
        .
        .
        .
    // after finger released
        case .end:
        // continue all animations using pan velocity with spring timing

        let normalizedPanVelocity: // how to normalize pan velocity
     runningAnimators.forEach { $0.continueAnimation(withTimingParameters: spring(for: velocity()), durationFactor: 0) }
    }
}

func velocity() -> CGVector {
    let pan = panRecognizer
    let progress = runningAnimators[0].fractionComplete
    let fraction = popupOffset*(1 - progress)
    return CGVector(with: pan.velocity(in: view), fraction: fraction)
}
func spring(for velocity: CGVector = .zero) -> UITimingCurveProvider {
    return UISpringTimingParameters(dampingRatio: 0.9, initialVelocity: velocity)//UISpringTimingParameters(mass: 2.5, stiffness: 80, damping: 25, initialVelocity: velocity)
}

the problem is when I quickly swipe up or down menu and release the finer, it seems animation hit the wall (slow quickly), then continue to rest

so how can I fix the issue? I have tried the whole day but I couldn't fix it

Upvotes: 4

Views: 1540

Answers (1)

Ichor de Dionysos
Ichor de Dionysos

Reputation: 1137

The documentation for the UISpringTimingParameters says: https://developer.apple.com/documentation/uikit/uispringtimingparameters/1649832-init

A vector with a magnitude of 1.0 corresponds to an initial velocity that would cover the total animation distance in one second. For example, if the total animation distance is 200 points and the view’s initial velocity is 100 points per second, specify a vector with a magnitude of 0.5.

Meaning that you have to normalize the velocity using the with of the view.

And looking at the official documentation for CGVector the initializer you are using is confusingly not documented. https://developer.apple.com/documentation/coregraphics/cgvector

What I've ended up doing was, calculating the normalized vector by myself. You would need to calculate the total points the view is moving from the start of the animation to the end and then using this distanceToMove to make a "unit vector" from it / normalizing it:

let distanceToMove = newY - oldY
let velocity = recognizer.velocity(in: view)
let relativeVelocityY = velocity.x / distanceToMove
let relativeVelocity = CGVector(dx: 0, dy: relativeVelocityY)
let timing = UISpringTimingParameters(dampingRatio: 0.9, initialVelocity: relativeVelocity)

Let me know if this worked for you.

Upvotes: 4

Related Questions