Search code examples
iphonecocoa-touchuikitmulti-touch

iPhone: Tracking/Identifying individual touches


I have a quick question regarding tracking touches on the iPhone and I seem to not be able to come to a conclusion on this, so any suggestions / ideas are greatly appreciated:

I want to be able to track and identify touches on the iphone, ie. basically every touch has a starting position and a current/moved position. Touches are stored in a std::vector and they shall be removed from the container, once they ended. Their position shall be updated once they move, but I still want to keep track of where they initially started (gesture recognition).

I am getting the touches from [event allTouches], thing is, the NSSet is unsorted and I seem not to be able to identify the touches that are already stored in the std::vector and refer to the touches in the NSSet (so I know which ones ended and shall be removed, or have been moved, etc.)

Here is my code, which works perfectly with only one finger on the touch screen, of course, but with more than one, I do get unpredictable results...

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

Thanks!


Solution

  • To fix your problem scrap your "handleTouches" method. The first thing you do in your handleTouches method, is switch it on the touchPhase, but that is already given to you. If you recieve the touch in touchesBegan, you know the touch is in UITouchPhaseBegan. By funneling touches from the four touch methods into one method, you are defeating the purpose of having four delegate methods.

    In each of those methods, Apple gives you an opportunity to deal with a different phase of the current touch.

    The second thing is that you don't need to search the event for the current touch, it is given to you as a parameter: touches.

    An event is comprised of sets of touches. For convienence, you are given the current touches even though it can also be found within event.

    So, in touchesBegan, you start tracking a touch.

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

    I'm using an array of dictionaries to hold my touch data.

    Try to seperate your code and move it into the appropriate touch method. For direction, Apple has a couple sample projects that focus on touches and show you how to setup those methods.

    Remember, these methods will get called automatically for each touch during each phase, you don't need to cycle through the event to find out what happened.

    The pointer to each set of touches remains constant, just the data changes.

    Also, I would read the iPhone OS programming guide section on event handling which goes into greater depth of what I said above with several diagrams explaining the relationship of touches to events over time.

    An excerpt:

    In iPhone OS, a UITouch object represents a touch, and a UIEvent object represents an event. An event object contains all touch objects for the current multi-touch sequence and can provide touch objects specific to a view or window (see Figure 3-2). A touch object is persistent for a given finger during a sequence, and UIKit mutates it as it tracks the finger throughout it. The touch attributes that change are the phase of the touch, its location in a view, its previous location, and its timestamp. Event-handling code evaluates these attributes to determine how to respond to the event.