使用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模拟器对此进行了测试。
答案 0 :(得分:18)
仅在发生平移时才定义velocityInView
方法。也就是说,只有当您实际移动手指时才会发生平移手势。如果你保持手指不动,它实际上不会触发平移手势。
这意味着没有内置方法来了解手势结束时的移动速度。您可以执行以下操作:检查上一个事件与状态值UIGestureRecognizerStateChanged
和UIGestureRecognizerStateEnded
之间的时差。然后,您可以调整此阈值以获得所需的行为。
例如
- (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;
}
}
答案 1 :(得分:1)
这是真正知道手指抬起时的速度的唯一方法:
有一些变量...
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)")
}
}
就是这样。据我所知,没有简单的方法。
+ 脚注。正如任何游戏程序员都会告诉您的那样,即使这样也有些伪劣。它提供了一帧的柏拉图式速度:纯粹主义者会在可讨论的帧数上使它平滑一点:/这是一个棘手的问题。