在 Xcode 中使用 UIPanGestureRecognizer 和速度来确定滑动方向过于敏感并且在方向之间快速切换

Posted

技术标签:

【中文标题】在 Xcode 中使用 UIPanGestureRecognizer 和速度来确定滑动方向过于敏感并且在方向之间快速切换【英文标题】:Using UIPanGestureRecognizer in Xcode and velocity to determine swipe direction is too sensitive and switches rapidly between directions 【发布时间】:2012-12-14 06:33:04 【问题描述】:

澄清:

我想要实现的是:滑动手势(向上、向下、向右和向左)位于 WebView 上。当任何手势发生时,这就是它需要发生的事情:一旦开始滑动,就必须开始一个动作(该动作实际上是加载一个 html 链接,该链接将 ip 摄像头移动到上、下、右、左)。 html链接使相机一直向右或向左或向上或向下。我有另一个 html 链接,加载时会告诉网络摄像机停止。

所以它需要发生的是当状态是 UIGestureRecognizerStateBegan 链接加载并不断移动相机直到用户没有触摸屏幕并且状态变为 UIGestureRecognizerStateEnded 并触发另一个 html 链接在 WebView 中运行哪个链接将停止相机移动。正如我们所说,我发布的初始代码就是这样做的,但是刷卡太敏感了。您的代码已解决此问题,但现在第二个链接在第一个链接之后立即加载,不允许相机移动。是否有意义??第一个链接需要运行直到手指被抬起。


原问题:

我试图在我管理的 UIPanGestureRecognizer 中使用速度确定滑动方向,它检测到右、左、上和下滑动,它会根据滑动方向触发适当的操作,并在 UIGestureRecognizerStateEnded 时再次触发正确的操作对于每次滑动,但是,我在下面的代码中的问题是方向非常敏感,例如在向右滑动期间,它可以识别大部分向右滑动,但也可以在两者之间向上或向下滑动。由于滑动并不总是完美地在一条直线上,所以我想发现的是,如果滑动大部分是向右并忽略微小的像素偏差,则只触发向右滑动的动作。

另外,对于我的项目来说,理想情况下是只触发一次给定滑动方向上的动作,就像在 UISwipeGestureRecognizer 中一样,但是使用它我遇到了 UIGestureRecognizerStateEnded 的问题,因为它不会让滑动动作运行,直到我抬起我的手指离开屏幕,即使我的手指仍在滑动,它也会立即完成滑动动作。

任何帮助将不胜感激。这是我的代码:

在 ViewDidLoad 我有:

UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
[_myWebView addGestureRecognizer:gestureRecognizer];
[gestureRecognizer setMinimumNumberOfTouches:1];
[gestureRecognizer setMaximumNumberOfTouches:1];

在我的课堂上我有:

- (void)handleGesture:(UIPanGestureRecognizer *)gestureRecognizer

CGPoint velocity = [gestureRecognizer velocityInView:_myWebView];

if(velocity.x > 0)//right

   //my action here

  if ( [gestureRecognizer state] == UIGestureRecognizerStateEnded )
   //my action when swipe finished here 

else if(velocity.x < 0)//left

   //my action here

  if ( [gestureRecognizer state] == UIGestureRecognizerStateEnded )
   //my action when swipe finished here 

else if(velocity.y > 0)//down

   //my action here

  if ( [gestureRecognizer state] == UIGestureRecognizerStateEnded )
   //my action when swipe finished here 

else if(velocity.y < 0)//up

   //my action here

  if ( [gestureRecognizer state] == UIGestureRecognizerStateEnded )
   //my action when swipe finished here 

谢谢,有什么问题请追问。

【问题讨论】:

【参考方案1】:

如果x 组件是y 组件的合理倍数,您可以进行一些“水平”方向检测。所以,也许如果xy 的五倍,那可以被认为是水平滑动。垂直方向反之亦然。 5 是否是正确的倍数取决于您(使用 tan-1,您可以看到从绝对水平/垂直转换为大约 11.3°),但从概念上讲,这是一种轻松解决问题的方法。

例如,这里有一个手势识别器,当用户开始向特定方向滑动时,该手势识别器将向相机发送命令以启动该方向的移动,并在用户将手指从屏幕上抬起时告诉相机停止:

CGFloat const gestureMinimumTranslation = 20.0;

typedef enum : NSInteger 
    kCameraMoveDirectionNone,
    kCameraMoveDirectionUp,
    kCameraMoveDirectionDown,
    kCameraMoveDirectionRight,
    kCameraMoveDirectionLeft
 CameraMoveDirection;

@interface ViewController ()

    CameraMoveDirection direction;

@end

@implementation ViewController

- (void)viewDidLoad

    [super viewDidLoad];

    UIPanGestureRecognizer *recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipe:)];
    [self.viewWithGestureRecognizer addGestureRecognizer:recognizer];


// This is my gesture recognizer handler, which detects movement in a particular
// direction, conceptually tells a camera to start moving in that direction
// and when the user lifts their finger off the screen, tells the camera to stop.

- (void)handleSwipe:(UIPanGestureRecognizer *)gesture

    CGPoint translation = [gesture translationInView:self.view];

    if (gesture.state == UIGestureRecognizerStateBegan)
    
        direction = kCameraMoveDirectionNone;
    
    else if (gesture.state == UIGestureRecognizerStateChanged && direction == kCameraMoveDirectionNone)
    
        direction = [self determineCameraDirectionIfNeeded:translation];

        // ok, now initiate movement in the direction indicated by the user's gesture

        switch (direction) 
            case kCameraMoveDirectionDown:
                NSLog(@"Start moving down");
                break;

            case kCameraMoveDirectionUp:
                NSLog(@"Start moving up");
                break;

            case kCameraMoveDirectionRight:
                NSLog(@"Start moving right");
                break;

            case kCameraMoveDirectionLeft:
                NSLog(@"Start moving left");
                break;

            default:
                break;
        
    
    else if (gesture.state == UIGestureRecognizerStateEnded)
    
        // now tell the camera to stop
        NSLog(@"Stop");
    


// This method will determine whether the direction of the user's swipe

- (CameraMoveDirection)determineCameraDirectionIfNeeded:(CGPoint)translation

    if (direction != kCameraMoveDirectionNone)
        return direction;

    // determine if horizontal swipe only if you meet some minimum velocity

    if (fabs(translation.x) > gestureMinimumTranslation)
    
        BOOL gestureHorizontal = NO;

        if (translation.y == 0.0)
            gestureHorizontal = YES;
        else
            gestureHorizontal = (fabs(translation.x / translation.y) > 5.0);

        if (gestureHorizontal)
        
            if (translation.x > 0.0)
                return kCameraMoveDirectionRight;
            else
                return kCameraMoveDirectionLeft;
        
    
    // determine if vertical swipe only if you meet some minimum velocity

    else if (fabs(translation.y) > gestureMinimumTranslation)
    
        BOOL gestureVertical = NO;

        if (translation.x == 0.0)
            gestureVertical = YES;
        else
            gestureVertical = (fabs(translation.y / translation.x) > 5.0);

        if (gestureVertical)
        
            if (translation.y > 0.0)
                return kCameraMoveDirectionDown;
            else
                return kCameraMoveDirectionUp;
        
    

    return direction;


@end

这演示了如何确定平移手势的初始方向,然后在确定初始方向以及手势结束时采取行动。

【讨论】:

好的,这个解决方案可以正确识别右、左、上和下,但我的其他问题仍然存在。该动作在识别出有效的滑动后立即开始,即使我的手指仍在滑动,也会立即结束,从而触发我的其他动作。我所追求的是当我开始滑动时我的第一个动作开始,并且只有在我的手指离开时才完成运行,当它触发我的第二个动作开始时。 感谢 Rob 的解决方案,对于我的滑动问题,现在可以正确识别滑动方向并且只触发一次而不是 15 次。上面代码的最大问题是,现在它无法识别,然后我的手指离开了屏幕,因此 UIGestureRecognizerStateEnded 仅在通过将手指从屏幕上抬起来完成滑动时才被调用,所以我的第二个动作开始了。希望我足够清楚,如果没有,我会尝试提供更多信息。 感谢您为我编辑它。我正要这样做,但我注意到你做到了。所以谢谢 用户只控制移动的方向(上、下、右、左)。该动作必须一直持续到手指抬起,否则会出现太多滑动而无法将相机移动 280 度。 Rob 你今天给我上了几节课。不仅通过修复和几乎重新输入我的代码,而且在寻求帮助时我也学会了更好地解释自己。我真的很感谢你的帮助,是的,代码正是我所追求的,并且工作完美!谢谢先生!【参考方案2】:

首先,您可以使用平移而不是手势的速度 - 这将为您提供从滑动开始到结束的距离,而不是滑动结束时的移动方向速度。

其次,您可能应该在检查方向之前检查手势状态是否为“结束”。现在您在每个“if”中重复相同的检查。

要获得方向,您可以使用带有 x 和 y 平移的 atan2 三角函数来获得平移角度,或者如果可以始终将手势解释为 4 个方向之一的滑动,只需查找 x 或y 具有较大的绝对值,然后是正值还是负值。

【讨论】:

OP 可能不想检查手势结束,否则最好使用滑动手势;这是 OP 试图解决的奇怪问题。就个人而言,我认为他应该使用标准的UISwipeGestureRecognizer,因为 OP 所需的手势非常不标准(通常用户应该始终能够在开始后有效地取消手势,而 OP 的解决方案不提供该功能)。但对每个人来说都是他自己的。 你是对的,但据我所知,他想在手势期间和结束时识别方向。这使我的第二点无效,关于使用平移,我不确定他是否想从一开始或在任何给定时刻测量运动的方向。当谈到测量方向时,你的答案确实完全没问题。我只能补充一点,为了减少方向的“随机性”,他可以保留最后几个手势位置的数组作为历史记录,并将最后一个与之前的第 n 个进行比较。 同意。我更新了我的答案,让手势识别器在成功识别手势后立即取消,以确保您没有发生任何无关的识别,例如,用户将手指从屏幕上抬起。

以上是关于在 Xcode 中使用 UIPanGestureRecognizer 和速度来确定滑动方向过于敏感并且在方向之间快速切换的主要内容,如果未能解决你的问题,请参考以下文章

iOS开发五种触屏事件的处理

如何在 Xcode 9 中使用 Xcode 服务器?我们是不是需要始终打开 Xcode 9?

在 Xcode 13 和 Xcode 12 中使用 previewCGImageRepresentation?

如何在 Xcode 12.5.1 或 Xcode 13 中使用 OSLog?

Xcode项目使用xcode项目时问题在其他mac上的其他xcode中启动

在设备中使用 xcode 4.5.2 运行 xcode 4.3.3 项目..(模拟器中没有问题)