Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A count of started touches is not equal to count of finished touches

I have the following code for testing purposes:

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

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

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

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self customTouchHandler:touches];
}
- (void)customTouchHandler:(NSSet *)touches
{
    for(UITouch* touch in touches){
        if(touch.phase == UITouchPhaseBegan)
            touchesStarted++;
        if(touch.phase == UITouchPhaseEnded || touch.phase == UITouchPhaseCancelled)
            touchesFinished++;
    }
    NSLog(@"%d / %d", touchesStarted, touchesFinished);
}

I suppose that when there is no touch on the screen, touchesStarted should be always equal to touchesFinished, but I have a pretty strange output:

2014-04-16 13:44:27.780 App[5925:60b] 2 / 0
2014-04-16 13:44:27.911 App[5925:60b] 2 / 1

I pressed the screen with two fingers and then released them almost (but not) in the same time.

Am I missing something? Multiple touches enabled for my view. By the way, the view is SKView and the code belongs to my custom SKScene.

UPDATE

As many of you cannot reproduce this strange behaviour, I've prepared a sample Xcode project: https://www.dropbox.com/s/qmgxka1gtgwquio/TapTest.zip

Try to tapping with two fingers at same time many times. touchesStarted must be equal to touchesEnded when you remove the fingers, right? But they are not. Here is my output:

2014-04-24 12:49:06.359 TapTest[8207:60b] 1 / 0
2014-04-24 12:49:06.376 TapTest[8207:60b] 2 / 0
2014-04-24 12:49:06.458 TapTest[8207:60b] 2 / 0
2014-04-24 12:49:06.460 TapTest[8207:60b] 2 / 1
2014-04-24 12:49:06.491 TapTest[8207:60b] 2 / 2
2014-04-24 12:49:07.325 TapTest[8207:60b] 3 / 2
2014-04-24 12:49:07.342 TapTest[8207:60b] 4 / 2
2014-04-24 12:49:07.408 TapTest[8207:60b] 4 / 2
2014-04-24 12:49:07.410 TapTest[8207:60b] 4 / 3
2014-04-24 12:49:07.426 TapTest[8207:60b] 4 / 3
2014-04-24 12:49:07.441 TapTest[8207:60b] 4 / 4
2014-04-24 12:49:07.842 TapTest[8207:60b] 6 / 4
2014-04-24 12:49:07.925 TapTest[8207:60b] 6 / 4
2014-04-24 12:49:07.941 TapTest[8207:60b] 6 / 5
2014-04-24 12:49:08.042 TapTest[8207:60b] 8 / 5
2014-04-24 12:49:08.125 TapTest[8207:60b] 8 / 6
2014-04-24 12:49:08.259 TapTest[8207:60b] 9 / 6
2014-04-24 12:49:08.293 TapTest[8207:60b] 9 / 6
2014-04-24 12:49:08.308 TapTest[8207:60b] 9 / 7
2014-04-24 12:49:08.425 TapTest[8207:60b] 10 / 7
2014-04-24 12:49:08.442 TapTest[8207:60b] 11 / 7
2014-04-24 12:49:08.444 TapTest[8207:60b] 11 / 7
2014-04-24 12:49:08.492 TapTest[8207:60b] 11 / 8
2014-04-24 12:49:08.575 TapTest[8207:60b] 11 / 9
2014-04-24 12:49:08.642 TapTest[8207:60b] 12 / 9
2014-04-24 12:49:08.659 TapTest[8207:60b] 13 / 9
2014-04-24 12:49:08.660 TapTest[8207:60b] 13 / 9
2014-04-24 12:49:08.692 TapTest[8207:60b] 13 / 9
2014-04-24 12:49:08.694 TapTest[8207:60b] 13 / 10
2014-04-24 12:49:08.708 TapTest[8207:60b] 13 / 10
2014-04-24 12:49:08.741 TapTest[8207:60b] 13 / 11
2014-04-24 12:49:08.792 TapTest[8207:60b] 14 / 11
2014-04-24 12:49:08.809 TapTest[8207:60b] 15 / 11
2014-04-24 12:49:08.810 TapTest[8207:60b] 15 / 11
2014-04-24 12:49:08.890 TapTest[8207:60b] 15 / 11
2014-04-24 12:49:08.892 TapTest[8207:60b] 15 / 12
2014-04-24 12:49:08.908 TapTest[8207:60b] 15 / 13
2014-04-24 12:49:09.042 TapTest[8207:60b] 17 / 13
2014-04-24 12:49:09.141 TapTest[8207:60b] 17 / 14
2014-04-24 12:49:09.242 TapTest[8207:60b] 19 / 14
2014-04-24 12:49:09.341 TapTest[8207:60b] 19 / 14
2014-04-24 12:49:09.358 TapTest[8207:60b] 19 / 15
2014-04-24 12:49:09.441 TapTest[8207:60b] 21 / 15
2014-04-24 12:49:09.525 TapTest[8207:60b] 21 / 15
2014-04-24 12:49:09.542 TapTest[8207:60b] 21 / 15
2014-04-24 12:49:09.559 TapTest[8207:60b] 21 / 16
2014-04-24 12:49:09.608 TapTest[8207:60b] 22 / 16
2014-04-24 12:49:09.625 TapTest[8207:60b] 23 / 16
2014-04-24 12:49:09.626 TapTest[8207:60b] 23 / 16
2014-04-24 12:49:09.708 TapTest[8207:60b] 23 / 16
2014-04-24 12:49:09.709 TapTest[8207:60b] 23 / 17
2014-04-24 12:49:09.774 TapTest[8207:60b] 23 / 18
2014-04-24 12:49:09.810 TapTest[8207:60b] 24 / 18
2014-04-24 12:49:09.826 TapTest[8207:60b] 25 / 18
2014-04-24 12:49:09.828 TapTest[8207:60b] 25 / 18
2014-04-24 12:49:09.908 TapTest[8207:60b] 25 / 18
2014-04-24 12:49:09.909 TapTest[8207:60b] 25 / 19
2014-04-24 12:49:09.974 TapTest[8207:60b] 25 / 20
2014-04-24 12:49:09.992 TapTest[8207:60b] 26 / 20
2014-04-24 12:49:10.026 TapTest[8207:60b] 27 / 20
2014-04-24 12:49:10.027 TapTest[8207:60b] 27 / 20
2014-04-24 12:49:10.091 TapTest[8207:60b] 27 / 20
2014-04-24 12:49:10.094 TapTest[8207:60b] 27 / 21
2014-04-24 12:49:10.125 TapTest[8207:60b] 27 / 22
like image 464
Andrey Gordeev Avatar asked Apr 16 '14 09:04

Andrey Gordeev


2 Answers

Also have this problem.

Only solution i found at this moment based on observing touch phase:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"Began %lu of %lu", [touches count], [event.allTouches count]);

    for (UITouch *touch in touches) {
        [touch addObserver:self forKeyPath:@"phase" options:0 context:nil];
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"Ended %lu of %lu", [touches count], [event.allTouches count]);
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

    if ([object isKindOfClass:[UITouch class]]) {
        UITouch *touch = object;
        NSLog(@"Touch %lu phase: %ld", (unsigned long)[touch hash], [touch phase]);
        if (touch.phase == UITouchPhaseEnded || touch.phase == UITouchPhaseCancelled) {
            NSLog(@"Touch %lu ended", (unsigned long)[touch hash]);
            [touch removeObserver:self forKeyPath:@"phase"];
        }
    }
}
like image 125
Nikolay Krasnov Avatar answered Nov 16 '22 00:11

Nikolay Krasnov


It appears this is a bug in SKView's touchesBegan: method. From what I can tell, Apple is using a dictionary to associate each UITouch with the object that the touch methods should be forwarded to. From my testing it appears that if there are multiple touches that begin at the same time, only one of the touches gets added to the dictionary. As a result, your scene won't ever receive the touch-related method calls for the other touches, which is why you are seeing the difference between touchesStarted and touchesFinished.

If you subclass SKView and override the touchesBegan: method, your scene should get the correct calls.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    for (UITouch *touch in touches) {
        NSSet *newSet = [NSSet setWithObject:touch];
        [super touchesBegan:newSet withEvent:event];
    }
}

This fixed the problem for me, but it's possible there might be some side effects from calling the super method multiple times.

like image 37
kevin. Avatar answered Nov 16 '22 00:11

kevin.