How to determine true end velocity of pan gesture?

一个人想着一个人 提交于 2019-11-30 05:53:32

The velocityInView method is defined only when a pan occurs. That is, only when you're actually moving the finger a pan gesture is occurring. If you keep your finger still, it does not actually trigger a pan gesture.

This means that there is no built-in method to know the movement speed when the gesture ends. You could do something like check the time difference between the last event with the status value as UIGestureRecognizerStateChanged and UIGestureRecognizerStateEnded. You can then tune this threshold in order to obtain the desired behavior.

For example

- (IBAction) panGestureRecognized:(UIPanGestureRecognizer *)recognizer {

    UIGestureRecognizerState state = recognizer.state;

    CGPoint gestureTranslation = [recognizer translationInView:self];
    CGPoint gestureVelocity = [recognizer velocityInView:self];

    if ( state == UIGestureRecognizerStateChanged )
         _lastChange = CFAbsoluteTimeGetCurrent();
    else if ( state == UIGestureRecognizerStateEnded ) {
         double curTime = CFAbsoluteTimeGetCurrent(); 
         double timeElapsed = curTime - _lastChange;
         if ( timeElapsed < MY_THRESHOLD )
              finalSpeed = gestureVelocity;
         else
              finalSpeed = CGPointZero;
    }   
 }

2019 how-to ...

This is the only way to really know the velocity when the finger comes up:

have some variables...

var cat: CADisplayLink? = nil
var prevTime = CFAbsoluteTimeGetCurrent()
var lastKnownPosition: CGFloat = 0
var lastKnownActualVelocity: Double = 0

and then ...

@objc func _checkVelocityEveryTrueFrame() {
    let newTime = CFAbsoluteTimeGetCurrent()
    let frameTime = newTime - prevTime
    prevTime = newTime

    let newPos = yourConstraint.constant
    lastKnownActualVelocity = Double(newPos - lastKnownPosition) / frameTime
    lastKnownPosition = newPos
    print("- \(frameTime) \(lastKnownPosition) \(lastKnownActualVelocity)")
}

@objc func dragOrFlick(_ p: UIPanGestureRecognizer) {
    if p.state == .began {
        cat?.invalidate()
        cat = nil
        cat = CADisplayLink(target: self,
             selector: #selector(_checkVelocityEveryTrueFrame))
        cat?.add(to: .main, forMode: .common)
    }

    if p.state == .changed {
        ... p.translation(in: ...
        yourConstraint.constant = new position...
    }

    if p.state == .ended {
        cat?.invalidate()
        cat = nil
        let trueFinalVelocity = lastKnownActualVelocity
        print("trueFinalVelocity is truly \(trueFinalVelocity)")
    }
}

That's it. As far as I know there's no simpler way.


+Footnote. As any game programmer will tell you, even this is a bit shoddy; it gives the platonic velocity over one frame: purists would smooth it a little over a discussable amount of frames :/ It's a tricky issue.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!