PDA

View Full Version : Tracking Multiple Touches




Darkroom
Dec 8, 2009, 09:59 AM
i'm trying to produce code that will allow for tracking of touches, and have the touches' coordinates displayed in the appropriate label (touchLabel1, touchLabel2, touchLabel3) according to the touches order.

one finger touches or moves around the screen and displays this first touch's coordinates in touchLabel1. if another finger touchs the screen, it's coordinates will be displayed in touchLabel2. if a third finger touches the screen, it's coordinates will be displayed in touchLabel3.

now for the part i'm having difficulty with: if all 3 fingers are touching the screen and displaying their own coordinates in the appropriate label, i want to be able to have the 2nd finger removed, set it's label back to "Touch 2: {0, 0}" while the first and third fingers continue to track their positions in their appropriate labels.

i have no idea how to write the touchesEnded: method for what i'm trying to accomplish.


- (void)viewDidLoad
{
labelsArray = [[NSArray alloc] initWithObjects:touchLabe1, touchLabe2, touchLabe3, nil];
touchesArray = [[NSMutableArray alloc] initWithCapacity:3];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
[touchesArray addObject:touch];

for (NSUInteger i = 0; i < 3; i++)
{
UILabel *aLabel = [labelsArray objectAtIndex:i];
if (i < [touchesArray count])
{
UITouch *touch = [touchesArray objectAtIndex:i];
NSString *point = NSStringFromCGPoint([touch locationInView:self.view]);
aLabel.text = [NSString stringWithFormat:@"Title %i:\n%@", i+1, point];
}
}
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (NSUInteger i = 0; i < 3; i++)
{
UILabel *aLabel = [labelsArray objectAtIndex:i];
if (i < [touchesArray count])
{
UITouch *touch = [touchesArray objectAtIndex:i];
NSString *point = NSStringFromCGPoint([touch locationInView:self.view]);
aLabel.text = [NSString stringWithFormat:@"Title %i:\n%@", i+1, point];
}
}
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//??
}



robbieduncan
Dec 8, 2009, 10:09 AM
Instead of relying on the position in the array to link the touch to a label store the touch/label mapping in a dictionary or similar. Then when the second touch is removed the mapping of the third touch to the third label will be unaffected.

Darkroom
Dec 8, 2009, 10:20 AM
in my "research" (read: aimlessly googling for guidance) i came across this:


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


but i'm not sure how to get the map the touches using dictionary keys.

robbieduncan
Dec 8, 2009, 10:25 AM
Well, get each touch from the set, create a NSString with the method you have below and insert that string and the first unused label you have into a dictionary. When the touch moves use the NSString (again created from the starting point) to update the correct label. Similarly when a touch ends remove that key (and value) from the dictionary.

Darkroom
Dec 8, 2009, 06:41 PM
i received a lot of help for this solution, so i am posting it here for others (project attached).


- (void)setUpTouchHandling
{
touchToLabelMapping = CFDictionaryCreateMutable (kCFAllocatorDefault, 5, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
availableLabels = [[NSMutableArray alloc] initWithObjects:touchLabel1, touchLabel2, touchLabel3, nil];
setUp = YES;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{

if (!setUp)
[self setUpTouchHandling];

for (UITouch *touch in touches)
{
if (touch.view == self.view)
{
UILabel *label = (UILabel *)[availableLabels objectAtIndex:0];
CFDictionaryAddValue (touchToLabelMapping, touch, label);
[availableLabels removeObjectAtIndex:0];
label.text = NSStringFromCGPoint([touch locationInView:self.view]);
}
}
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
{
if (touch.view == self.view)
{
UILabel *label = (UILabel *)CFDictionaryGetValue(touchToLabelMapping, touch);
label.text = NSStringFromCGPoint([touch locationInView:self.view]);
}
}
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
{
if (touch.view == self.view)
{
UILabel *label = (UILabel *)CFDictionaryGetValue(touchToLabelMapping, touch);
label.text = @"{0, 0}";
CFDictionaryRemoveValue (touchToLabelMapping, touch);
[availableLabels insertObject:label atIndex:0];
}
}
}

robbieduncan
Dec 9, 2009, 04:10 AM
Glad you got that working. I was wondering why you decided to use the CFDictionary functions instead of an NSMutableDictionary?

Darkroom
Dec 9, 2009, 04:18 AM
Glad you got that working. I was wondering why you decided to use the CFDictionary functions instead of an NSMutableDictionary?

another (smarter) developer wrote the solution, but the following is from apple's documentation for Handling a Complex Multi-Touch Sequence (http://developer.apple.com/iphone/library/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/EventHandling/EventHandling.html#//apple_ref/doc/uid/TP40007072-CH9-SW16):

When handling an event with multiple touches, you often store initial bits of each touch’s state for later comparison with the mutated UITouch instance. As an example, say you want to compare the final location of each touch with its original location. In the touchesBegan:withEvent: method, you can obtain the original location of each touch from the locationInView: method and store those in a CFDictionaryRef object using the addresses of the UITouch objects as keys. Then, in the touchesEnded:withEvent: method you can use the address of each passed-in UITouch object to obtain the object’s original location and compare that with its current location. (You should use a CFDictionaryRef type rather than an NSDictionary object; the latter copies its keys, but the UITouch class does not adopt the NSCopying protocol, which is required for object copying.)

Darkroom
Dec 10, 2009, 07:06 PM
i'm noticing a memory leaks with this solution while testing on device, originating in the touchesEnded method. however, i haven't yet been able to constantly reproduce the leak and i don't see anywhere in the code that would cause a leak.

any ideas?