如何确定平移手势的真实结束速度?

Posted

技术标签:

【中文标题】如何确定平移手势的真实结束速度?【英文标题】:How to determine true end velocity of pan gesture? 【发布时间】:2013-09-30 10:51:59 【问题描述】:

当使用UIPanGestureRecognizer并检测UIGestureRecognizerStateEnded时,手势的速度不是真实的速度。相反,它是之前调用我的操作方法的旧速度。如何在手势结束时获取真实速度?

我这样创建UIPanGestureRecognizer

    UIPanGestureRecognizer* panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureRecognized:)];
    [panGestureRecognizer setMaximumNumberOfTouches:2];
    [panGestureRecognizer setMinimumNumberOfTouches:1];
    [panGestureRecognizer setDelegate:self];
    [panGestureRecognizer setDelaysTouchesBegan:NO];
    [panGestureRecognizer setDelaysTouchesEnded:NO];
    [panGestureRecognizer setCancelsTouchesInView:NO];
    [self addGestureRecognizer:panGestureRecognizer];

我的动作方法的开始在这里:

- (IBAction) panGestureRecognized:(UIPanGestureRecognizer *)recognizer 

    UIGestureRecognizerState state = recognizer.state;

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

    [CBAppDelegate log:@"panGestureRecognized: state: %s\n    translation: (%f, %f)\n    velocity: (%f, %f)", [self toString:state], gestureTranslation.x, gestureTranslation.y, gestureVelocity.x, gestureVelocity.y];

日志输出示例:

2013-09-30_10:46:32.830 panGestureRecognized: state: UIGestureRecognizerStateChanged
    translation: (-283.000000, 2.000000)
    velocity: (-43.046783, 45.551472)
2013-09-30_10:47:02.942 panGestureRecognized: state: UIGestureRecognizerStateEnded
    translation: (-283.000000, 2.000000)
    velocity: (-43.046783, 45.551472)

如您所见,两个日志条目中的速度相同(翻译相同,但我只关心速度),尽管我按住手指大约 30 秒没有移动它,然后抬起手指手指。您可以从条目的时间戳中判断时间。在我的手指不动 30 秒后肯定不会报告速度。

我已经用 ios 6.1 的 iPhone 的 iOS 模拟器对此进行了测试。

【问题讨论】:

您可以使用时间戳自己计算。对于长期持有,如果起始时间戳足够长,您可以重置起始时间戳,并计算您想要的任何内容。祝你好运! 五年来没人回答这个问题真是太神奇了!我已经提出了更好、更正确、更现代的答案.. 2019 【参考方案1】:

velocityInView 方法仅在平移发生时定义。也就是说,只有当您实际移动手指时,才会发生平移手势。如果您保持手指不动,它实际上不会触发平移手势。

这意味着没有内置方法可以知道手势结束时的移动速度。您可以执行一些操作,例如检查状态值为UIGestureRecognizerStateChangedUIGestureRecognizerStateEnded 的最后一个事件之间的时间差。然后,您可以调整此阈值以获得所需的行为。

例如

- (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;
       
 

【讨论】:

很好的答案!我能知道你为 MY_THRESHOLD 设置的值是多少吗? 恐怕我只是为这个问题创建了这个玩具示例,我实际上不必自己使用它!您也许可以使用不同的值,看看哪一个效果最好 嘿,你是如何将你的 uitableview(或者我猜它的超类,uiscrollview)与 pangesture IBOutlet 链接起来的 这真的只是一个粗略的解决方案。我已经采取了唯一的方式(我知道)来实际做到这一点:/【参考方案2】:

2019 年操作方法...

这是真正知道手指抬起时速度的唯一方法:

有一些变量...

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

然后……

@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)")
    

就是这样。据我所知,没有更简单的方法。

+脚注。正如任何游戏程序员都会告诉你的那样,即使这也有点粗制滥造;它在一帧上给出了柏拉图式的速度:纯粹主义者会在可讨论的帧数上稍微平滑它:/这是一个棘手的问题。

【讨论】:

以上是关于如何确定平移手势的真实结束速度?的主要内容,如果未能解决你的问题,请参考以下文章

iOS:啥速度阈值使平移手势成为轻弹?

如何延迟/减慢 UIScrollView 平移速度

UI 平移手势不起作用

处理嵌套 UIScrollViews 中的冲突手势

UIPanGestureRecognizer中的Velocity和translation开始状态

模仿照片应用程序的平移行为(速度)