I have the following code for testing purposes:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self customTouchHandler:touches];
}
- (v
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.
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.
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"];
}
}
}
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.
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.