3

我正在尝试创建一个能够检测 4 个手指旋转的手势识别器(旋转音量旋钮时类似)。
主要思想是创建 UIRotateGestureRecognizer 的子类并覆盖其方法。在-touchesBeganI 检测触摸次数中,如果次数低于 4,则手势状态为失败。
之后,我将位置点传递给找到凸包直径的算法。如果你考虑一下,你的手指就是顶点,我只需要找到最大距离的两个顶点。获得这两个点后,我将它们作为 ivar 引用,并将它们传递给超类,因为它只是用两个手指进行的简单旋转。
它不起作用:

  1. 触摸的检测似乎很难
  2. 很少-touchesHasMoved被称为
  3. 当它被调用时,它大部分时间都挂起

有人能帮我吗?

这是代码:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if (touches.count<4) {
        //FAIL
        self.state = UIGestureRecognizerStateFailed;
        return;
    }

    //Find the diameter of the convex hull
    NSArray * touchesArray = [touches allObjects];
    NSMutableArray * pointsArray = @[].mutableCopy;
    for (UITouch * touch in touchesArray) {
        [pointsArray addObject:[NSValue valueWithCGPoint:[touch locationInView:touch.view]]];
    }
    DiameterType convexHullDiameter = getDiameterFromPoints(pointsArray);
    CGPoint firstPoint =  convexHullDiameter.firstPoint;
    CGPoint secondPoint = convexHullDiameter.secondPoint;
    for (UITouch * touch in touchesArray) {
        if (CGPointEqualToPoint([touch locationInView:touch.view], firstPoint) ) {
            self.fistTouch = touch;
        }
        else if (CGPointEqualToPoint([touch locationInView:touch.view], secondPoint)){
            self.secondTouch = touch;
        }
    }
    //Calculating the rotation center as a mid point between the diameter vertices
    CGPoint rotationCenter = (CGPoint) {
        .x = (convexHullDiameter.firstPoint.x + convexHullDiameter.secondPoint.x)/2,
        .y = (convexHullDiameter.firstPoint.y + convexHullDiameter.secondPoint.y)/2
    };
    self.rotationCenter = rotationCenter;
    //Passing touches to super as a fake rotation gesture
    NSSet * touchesSet = [[NSSet alloc] initWithObjects:self.fistTouch, self.secondTouch, nil];
    [super touchesBegan:touchesSet withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    if (touches.count<4) {
        self.state = UIGestureRecognizerStateFailed;
        return;
    }

    [super touchesMoved:[[NSSet alloc] initWithObjects:self.fistTouch, self.secondTouch, nil] withEvent:event];
}

- (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event  {
    [super touchesCancelled:[[NSSet alloc] initWithObjects:self.fistTouch, self.secondTouch, nil] withEvent:event];
}

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event  {
    [super touchesEnded:[[NSSet alloc] initWithObjects:self.fistTouch, self.secondTouch, nil] withEvent:event];
}
4

3 回答 3

2

初始检测很难的原因是所有触摸可能不会同时开始。touchesBegan 可能会被多次调用,因为单独的触摸落在屏幕上。您可以通过 event.allTouches 使用 event 参数来查询所有当前的触摸。因此,您当前触发手势失败的方法将不起作用。如果 touches.count < 4,则不应将状态设置为失败,而应在 event.allTouches.count < 4 时返回。如果在一定时间内没有发生第四次触摸,则可以使用计时器将状态设置为失败首先。

touchesMoved 可能有问题,因为事件对象中的触摸与您传递给 super 的集合中的触摸不匹配。

于 2013-12-02T06:02:01.547 回答
1

如果你考虑一下,你的手指就是顶点,我只需要找到最大距离的两个顶点。

我认为这在实践中是行不通的,即使你能够欺骗UIGestureRecognizer.

这就是我将以“正确”方式实现算法的方式:

  1. 记住“旧”的接触。
  2. 当您获得“新”触摸时,请尝试将每个手指与之前的触摸匹配。如果做不到,就失败。
  3. 计算“新”+“旧”触摸的中心。
  4. 对于两步前确定的 4 个手指中的每一个,计算以弧度为单位的移动角度,近似为

    new(i) - old(i) divided by distance to center

  5. 如果任何角度太大(> 0.5),则失败。

  6. 这保证了近似值是有效的。
  7. 现在计算 4 个角度的平均值。

恭喜,您现在有了旋转角度(以弧度为单位)。

于 2013-11-26T20:14:18.247 回答
0

如果我有足够的众议员,我会把它放在评论中。

[super touchesMoved:[[NSSet alloc] initWithObjects:self.fistTouch, self.secondTouch, nil] withEvent:event];

您正在使用名为 的东西fistTouch,这听起来不像您想要的。我的猜测是你想要firstTouch的。

此外,正在进行的手势之间可能会发生冲突,这些冲突可能会相互覆盖。你知道 iOS7 中有一个 4 指缩小是系统范围的手势吗?此外,应用程序期间的 4 指放大将关闭它。

于 2013-11-26T14:11:32.280 回答