iPhone:跟踪/识别单个触摸

14

关于在iPhone上跟踪触摸操作,我有一个问题。我似乎无法得出结论,因此非常感谢任何建议/想法:

我想能够跟踪和识别iPhone上的触摸操作,即基本上每个触摸都有一个起始位置和当前/移动位置。触摸存储在std :: vector中,一旦它们结束,它们将从容器中删除。一旦它们移动,它们的位置应该更新,但我仍然想跟踪它们最初的位置(手势识别)。

我正在从[event allTouches]获取触摸,问题是,NSSet是无序的,我似乎无法识别已经存储在std :: vector中的触摸,并引用NSSet中的触摸(这样我就知道哪些触摸已经结束并且应该被删除,或者已经被移动等等)

以下是我的代码,当然,在触摸屏上只有一个手指时,它可以完美运行,但是当有多个手指时,我会得到不可预测的结果......

    - (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
    [self handleTouches:[event allTouches]];
}

- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
    [self handleTouches:[event allTouches]];
}

- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
    [self handleTouches:[event allTouches]];
}

- (void) touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event
{
    [self handleTouches:[event allTouches]];
}

- (void) handleTouches:(NSSet*)allTouches
{   
    for(int i = 0; i < (int)[allTouches count]; ++i)
    {
        UITouch* touch = [[allTouches allObjects] objectAtIndex:i];
        NSTimeInterval timestamp = [touch timestamp];

        CGPoint currentLocation = [touch locationInView:self];
        CGPoint previousLocation = [touch previousLocationInView:self];

        if([touch phase] == UITouchPhaseBegan)
        {
            Finger finger;
            finger.start.x = currentLocation.x;
            finger.start.y = currentLocation.y;
            finger.end = finger.start;
            finger.hasMoved = false;
            finger.hasEnded = false;

            touchScreen->AddFinger(finger);
        }
        else if([touch phase] == UITouchPhaseEnded || [touch phase] == UITouchPhaseCancelled)
        {
            Finger& finger = touchScreen->GetFingerHandle(i);

            finger.hasEnded = true;
        }
        else if([touch phase] == UITouchPhaseMoved)
        {
            Finger& finger = touchScreen->GetFingerHandle(i);

            finger.end.x = currentLocation.x;
            finger.end.y = currentLocation.y;
            finger.hasMoved = true;
        }
    }

    touchScreen->RemoveEnded();
}

谢谢!

3个回答

8

看起来追踪多点触摸的“正确”方法是通过UITouch事件的指针值。

您可以在此Apple开发者文档的“处理复杂的多点触摸序列”部分中找到更多详细信息。


1
如果你只追踪之前的位置并使用它来查找相关的触摸,那么你不会成功。通常touchesEnded会传递一个与所有缓存值不同的位置。谷歌搜索“处理复杂的多点触控序列”以查找文档;链接会变动。Erica Sadun写了关于这个问题的文章:http://blogs.oreilly.com/iphone/2008/10/recovering-multiple-touches.html,并提供了一些代码:http://github.com/erica/iphone-3.0-cookbook-/blob/master/C08-Gestures/14-Resize%20And%20Rotate/main.m我猜苹果希望能重新设计触摸API,以传递NSArray而不是NSSet。 - Graham Perks

7
为了解决您的问题,请放弃您的"handleTouches"方法。您在handleTouches方法中做的第一件事是根据touchPhase进行切换,但这已经为您提供。如果您在touchesBegan中接收到触摸,则知道触摸处于UITouchPhaseBegan状态。通过将四个触摸方法的触摸汇集到一个方法中,您正在破坏具有四个委托方法的目的。
在每个方法中,Apple都为您提供了处理当前触摸不同阶段的机会。
第二件事是,您不需要搜索事件以获取当前触摸,它作为参数给出:touches。
事件由一组触摸组成。为了方便起见,即使可以在事件中找到当前触摸,您也会得到当前触摸。
因此,在touchesBegan中,您开始跟踪触摸。
    - (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event{


        NSString *startPoint = NSStringFromCGPoint([[touches anyObject] locationInView:self]);  

        NSDictionary * touchData = [NSDictionary dictionaryWithObjectsandKeys: startPoint, @"location", touches, @"touch"]

        [startingLocations addObject:touchData];

        }

我正在使用一个字典数组来保存我的触摸数据。
尝试将您的代码分离并移动到适当的触摸方法中。为了方向,苹果公司有一些关于触摸的示例项目,展示了如何设置这些方法。
记住,这些方法会自动调用每个阶段的每个触摸,您不需要循环遍历事件以找出发生了什么。
每组触摸的指针保持不变,只是数据改变。
此外,我建议阅读iPhone OS编程指南中关于事件处理的部分,其中更详细地解释了上述内容,并提供了几个图表,说明触摸与事件随时间的关系。
以下是一段摘录:
在iPhone OS中,UITouch对象代表一个触摸,UIEvent对象代表一个事件。事件对象包含当前多点触摸序列的所有触摸对象,并且可以提供特定于视图或窗口的触摸对象(参见图3-2)。在序列期间,对于给定的手指,触摸对象是持久的,并且UIKit在跟踪手指时对其进行变异。更改的触摸属性是触摸的阶段、其在视图中的位置、其先前的位置和时间戳。事件处理代码评估这些属性以确定如何响应事件。

1
我同意你的观点。我将我的代码分成了适当的方法。然而,我的问题是我需要创建Objective-C和C++之间的链接,并且我的游戏架构要求对输入进行轮询,而不是在事件被触发后处理它们。这就是为什么我将触摸存储在STL向量中的原因。无论如何,你提供的UITouch对象随时间持久存在的信息帮了我很大的忙。现在,我正在使用对象地址作为键来唯一标识一个触摸。效果非常好。谢谢。 - FlorianZ
如果您在touchesBegan中收到触摸,则知道该触摸处于UITouchPhaseBegan阶段 - 真的吗?这听起来对我来说不正确 - 根据Apple的文档,他们似乎说相反:使用MultiTouch,来自touchesBegan的触摸可能处于任何阶段。 - Adam
@Adam,你可能误读了。虽然事件中包含的触摸可能处于另一个阶段,但“touches”参数中的所有触摸都保证处于指定的阶段。根据《事件处理指南》:“如果由于某种原因您对当前多点触控序列中未发生变化或处于传入集合中所述阶段之外的触摸感兴趣,则可以从传入的UIEvent对象中请求它们。” - Corey Floyd
@Corey - 谢谢。我误解了一段时间:(。我进行了测试以进行检查,并且它的行为与您引用的文档描述相同。 - Adam

0

您应该能够通过存储所有触摸的先前位置并在检测到新触摸时比较这些先前位置来正确整理您的触摸。

在您的-handleTouches方法中,您可以在for循环中添加类似以下内容的代码:

// ..existing code..
CGPoint previousLocation = [touch previousLocationInView:self];

// Loop through previous touches
for (int j = 0; j < [previousTouchLocationArray count]; j++) {
  if (previousLocation == [previousTouchLocationArray objectAtIndex:j]) {
    // Current touch matches - retrieve from finger handle j and update position
  }
}

// If touch was not found, create a new Finger and associated entry 

显然,您需要做一些工作将其集成到您的代码中,但我相信您可以使用这个想法正确地识别屏幕上移动的触摸。另外,我刚刚意识到CGPoint不会很好地适应NSArray - 您需要将它们包装在NSValue对象中(或使用不同类型的数组)。


好主意。但出于某些原因,这对我不起作用。位置有时似乎是错的。这可能是由于性能降低,当我渲染到OpenGL上下文时(我在制作游戏)。也许触摸事件会被忽略,之前记录的位置就不再对应了?还是谢谢你! - FlorianZ
2
这对我也造成了问题,特别是当你在屏幕上移动很多手指时。有时候你会得到一个与之前任何位置都不匹配的位置。 - Nosredna

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接