A count of started touches is not equal to count o

2019-03-18 03:45发布

问题:

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

回答1:

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


回答2:

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.



回答3:

Cannot comment, so will answer here. You coud print all handlers like this, maybe it helps:

- (void)customTouchHandler:(NSSet *)touches
{
    static int handlerCounter = 0;
    handlerCounter++;
    for(UITouch* touch in touches){
        NSLog(@"%d: %d", handlerCounter, touch.phase);
        if(touch.phase == UITouchPhaseBegan)
            touchesStarted++;
        if(touch.phase == UITouchPhaseEnded || touch.phase == UITouchPhaseCancelled)
            touchesFinished++;
    }
    NSLog(@"%d / %d", touchesStarted, touchesFinished);
}

Is it possible to switch(touch.phase) in obj-c? Is the "or" in the second if evaluated correctly, try using brackts.



回答4:

If you are using more than one finger you should include self.view.multipleTouchEnabled = YES; in your init.

I ran some extensive tests on your supplied code and the only time the numbers do not match up is when the taping occurs very fast from a combination of either one or both fingers. In this case, iOS looses track of the contact the current state.

This is very similar to when a sprite bounces up and down very quickly on another sprite which sometimes causes a wrong contact state in the didBeginContact.

There is currently no way you can get around this using multiple touches. If you use a single touch, your count will always (at least in my tests) match.



回答5:

I had this same problem before (touchesEnded:withEvent: is not called).

My testing revealed that touchedEnded was not being called 100% of the time. My solution: Place the four touch handling methods in the AppDelegate.m file and forward the touches to your view controller. I have no idea why this works, but I didn't have any issues after doing that.