I'm using spriteKit for my game, I detect single tap and double tap by using the following code:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
if (touch.tapCount == 1){
[self.tapQueue addObject:#1];
NSLog(#"touch.tapCount == 1 :)");
}
if (touch.tapCount == 2) {
[self.tapQueue addObject:#2];
NSLog(#"touch.tapCount == 2 :)");
}
}
-(void)processUserTapsForUpdate:(NSTimeInterval)currentTime {
for (NSNumber* tapCount in [self.tapQueue copy]) {
if ([tapCount unsignedIntegerValue] == 1)
[self singleTap];
if ([tapCount unsignedIntegerValue] == 2)
[self doubleTap];
[self.tapQueue removeObject:tapCount];
}
}
This code detect single tap but when it detect double tap it detect single tap with it. How can I difference between single tap and double tap?
Thanks
Are you saying that a single tap is reported twice during a double tap, once for the single tap and once for the double tap, before the double tap is reported?
Also to try answering your question, create a global tapCount instead of relying on the tapCount from the touch events. Or a boolean var tappedOnce where you check to make sure its double tapped.
Related
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *aTouch in touches) {
if (aTouch.tapCount >= 2) {
// The view responds to the tap
}
}
}
I'm using the code above to detect double tap gesture; however, how can I set the code to happen only once?
In other words, when you tap once, the character jumps. When you tap twice in quick succession, the character will double jump. But how do you set the taps in a way that the character will not continuously double jump and go higher off the single-view without initially taping once?
A very simple approach of achieving this is by declaring a global bool variable and set its value once the double tap has been detected!
Something like this:
#interface MyViewController()
{
bool isTapped;
}
#end
#implementation MyViewController
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *aTouch in touches) {
if (aTouch.tapCount >= 2) {
if(!isTapped) {
// The view responds to the tap
isTapped = YES;
}
}
}
}
#end
Hope this helps
Not sure if this helps and also just take one flag and set it yes or no accordingly:-
UILongPressGestureRecognizer *tapRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
tapRecognizer.delegate = self;
tapRecognizer.minimumPressDuration = //Up to you;
[self.someView addGestureRecognizer:tapRecognizer];
I have an NSMutableArray filled with different sprites. These sprites are all on the screen. How can I detect if a touch is landing on one of these sprites, and then do something if a touch on the sprite has occurred?
This is what I have now,
CGPoint touchLocation = [touch locationInNode:_physicsNode];
if(CGRectContainsPoint((starInArray.boundingBox), touchLocation)) {
Instead of (starInArray.boundingBox), I want to be able to say something like (anyObjectInMyArray.boundingBox).
Any way to go about this?
Thanks!
Something along the lines of this should work.
- (BOOL) ccTouchBegan:(UITouch *)touches withEvent:(UIEvent *)event
{
CGPoint touchLocation = [self convertTouchToNodeSpace:touches];
for (CCSprite *star in starInArray)
{
if (CGRectContainsPoint(CGRectMake(star.position.x - star.contentSize.width/2,
star.position.y - star.contentSize.height/2, star.contentSize.width, star.contentSize.height), touchLocation))
{
//Do Something
break;
}
}
}
I'd really like to get to the bottom of why this code causes intermittent response to touch input... Even with NSLog as the first instruction...
I've just made a new project in cocos2d with Box2d for a game that at this stage needs to just a few simple things...
A basket that appears in the centre of the screen. It must be a b2Fixture and fall onto a surface. Then if the user touched the screen, I want the basket to zoom to the touch point, and from there the user can drag it around the screen.
When the user lets go, the basket drops... I have this working right now...
However the BUG is that touching the screen doesn't always work... It intermittently responds to touches, and therefore intermittently calls the methods.
As you will see below, I have used NSLog to check when each methods are being called. The result is that sometimes you have to lift your finger off the screen and then back on several times, and then "seemingly at random", it will decide to run the code....
Heres what I got...
My touch methods....
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Multi Touch Moved...");
if (_mouseJoint == NULL) return;
UITouch *myTouch = [touches anyObject];
CGPoint location = [myTouch locationInView:[myTouch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
b2Vec2 locationWorld = b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO);
_mouseJoint->SetTarget(locationWorld);
}
-(void)ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event
{
NSLog(#"\nThe touch was CANCELED...");
}
-(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
NSLog(#"Single Touch Moved...");
}
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *myTouch = [touches anyObject];
CGPoint location = [myTouch locationInView:[myTouch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
b2Vec2 locationWorld = b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO);
NSLog(#"\n\nTouch did begin...");
if (_mouseJoint != NULL)
{
_mouseJoint->SetTarget(locationWorld);
NSLog(#"The IF statment was met...");
return;
}
NSLog(#"The IF statment was NOT met...Running _mouseJoint setup...");
b2MouseJointDef md;
md.bodyA = _groundBody;
md.bodyB = _body;
md.target = _body->GetPosition();
md.collideConnected = true;
md.maxForce = 100000000.0f * _body->GetMass();
_mouseJoint = (b2MouseJoint *)_world->CreateJoint(&md);
_body->SetAwake(true);
_mouseJoint->SetTarget(locationWorld);
}
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (_mouseJoint != nil) {
_world->DestroyJoint(_mouseJoint);
_mouseJoint = nil;
}
}
And this is the interface that some of the code refers to...
#interface HelloWorldLayer : CCLayerColor
{
b2World *_world;
b2Body *_body;
b2Fixture *_bodyFix;
b2MouseJoint *_mouseJoint;
b2Body *_groundBody;
}
The only idea another member helped me determine is, that because I'm working within a single scene, and I have/had a CCMenu and a CCLabelTTF on the screen, is it possible that the CCMenu is still intercepting touches, and if so, how can I destroy the CCMenu after my animation has finished?
Pressing the only button item simply CCmoves the title and label(CCMenuItem) off the screen vertically... But the object still exists...
The problem here was that my CCMenu was still receiving touches (I assume in parts of the screen where the CCMenuItems where formed).
The solution was to declare my CCMenu in my #implementation rather than in my init scene setup.
#implementation HelloWorldLayer
{
CCLabelTTF *label;
CCLabelTTF *startTheGameLabel;
CCSprite *theCattery;
CCMenu *_menu;
}
Then in my init, rather than declaring it there, i simply assign to the one in the #implementation.
-(id) init { if( (self=[super initWithColor:ccc4(50, 180, 220, 255)]) )
{
//.. Other "irrelevant to question" scene setup stuff here...//
// Start the game button
startTheGameLabel = [CCLabelTTF labelWithString:#"Save Some Kitties!" fontName:#"Zapfino" fontSize:20];
CCMenuItemLabel *startTheGameLabelItem = [CCMenuItemLabel itemWithLabel:startTheGameLabel target:self selector:#selector(startGameStub:)];
// Push the menu
_menu = [CCMenu menuWithItems: startTheGameLabelItem, nil];
[self addChild:_menu];
//.. Other "irrelevant to question" scene setup stuff here...//
}
This gives me access to the CCMenu throughout my class, so I can later disable touch input once the user has made a selection.
-(void) startGameStub:(id)sender
{
CGSize size = [[CCDirector sharedDirector] winSize];
// Clear the labels off the screen
CMoveTo *moveTheTitleLabelAction = [CCMoveTo actionWithDuration:1.0 position:ccp(label.position.x, size.height + label.boundingBox.size.height)];
CCMoveTo *moveTheStartLabelAction = [CCMoveTo actionWithDuration:1.0 position:ccp(startTheGameLabel.position.x, size.height + startTheGameLabel.boundingBox.size.height)];
// Commit actions
[label runAction:moveTheTitleLabelAction];
[startTheGameLabel runAction:moveTheStartLabelAction];
// LIFE SAVING MESSAGE!!!
[_menu setTouchEnabled:NO]; // This is what fixes the problem
}
I have subclassed a UIView that already handles single touches and drags. I want to enhance the interaction of this view so that, while dragging, if the user touches with a second finger (anywhere else in the view), then the system prints a message. I've made a stab at the code:
In my header file I've declared:
NSString *key; // This unique key identifies the first touch
My .m file I have:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *t in touches) {
if (key == NULL)
{
key = [[[NSValue valueWithPointer:t] description] copy];
}
if ([key isEqualToString:[[NSValue valueWithPointer:t] description]])
{
NSLog(#"calling parent to handle single touch");
[super touchesBegan:[NSSet setWithObject:t] withEvent:event];
}
else
{
[self twoTouchDetected];
}
}
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *t in touches) {
if ([key isEqualToString:[[NSValue valueWithPointer:t] description]])
{
[super touchesMoved:[NSSet setWithObject:t] withEvent:event];
}
}
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *t in touches) {
if ([key isEqualToString:[[NSValue valueWithPointer:t] description]])
{
[super touchesEnded:[NSSet setWithObject:t] withEvent:event];
key = NULL;
}
}
}
Unfortunately there are issues with this implementation. The first time (while dragging with one finger) if I touch with the second figer, the system will register it immediately. However, the second time I touch with a second finger (while still continuing to drag with the first finger), the second finger touch does not register until the first finger is lifted up. The events from the second finger are backed up...
What is also strange is that sometimes, the parent gets called with touch data from the 2nd finger and not the 1st.
It turns out my code worked, but the problem was that I subclassed an object belonging to the Core Plot framework. This framework does weird things to their objects and therefore the touches were coming back in the wrong order.
I created an empty project to receive touches and everything came out great.
I'm having trouble getting a UIView to respond how I want with multiple touches. Basically certain UITouches are in UITouchPhaseBegan but never make it to UITouchPhaseEnded or UITouchPhaseCancelled. Here's the code I'm using to handle touches, which is called from touchesBegan:withEvent, touchesMoved:withEvent, touchesEnded:withEvent and touchesCancelled:withEvent. If I put one finger down, then another, move them, and release them simultaneously, the NSLog output is sometimes Began! Began! Ended! rather than Began! Began! Ended! Ended!. Are these touches getting lost somewhere? How can I keep track of them?
- (void) handleTouchEvent:(UIEvent *)event {
for( UITouch* touch in [event allTouches] ) {
if( touch.phase == UITouchPhaseBegan ) {
NSLog(#"Began!");
if( ![m_pCurrentTouches containsObject:touch] )
[m_pCurrentTouches addObject:touch];
uint iVoice= [m_pCurrentTouches indexOfObject:touch];
CGPoint location = [touch locationInView:self];
m_pTouchPad->SetTouchPoint( location.x, location.y, iVoice );
m_pTouchPad->SetIsTouching( true, iVoice );
}
else if( touch.phase == UITouchPhaseMoved ) {
uint index= [m_pCurrentTouches indexOfObject:touch];
CGPoint location = [touch locationInView:self];
m_pTouchPad->SetTouchPoint( location.x, location.y, index );
}
else if( touch.phase == UITouchPhaseEnded || touch.phase == UITouchPhaseCancelled ) {
uint index= [m_pCurrentTouches indexOfObject:touch];
[m_pCurrentTouches removeObject:touch];
NSLog(#"Ended!");
m_pTouchPad->SetIsTouching( false, index );
}
}
}
EDIT:
I'm offering a bounty because I really want a good solution to this. To summarize: I need a system where every touch that begins also ends, so if a user puts down one finger and then another elsewhere, I can see both touches begin, and by the time there are no fingers in contact with the device anymore, I have seen both touches end.
Am I pursuing the wrong strategy to achieve this?
One event can report many touches. So you are sometimes getting "Ended!" once, because only one event arrived and only one touch event handler call was made - but it reported both touches ending. If you are manually handling multiple simultaneous touches (fingers), it is up to you to track each touch individually and check every touch in every event to see how many of your touches are being reported and decide what to do.
Apple has sample code showing how to do this by maintaining a CFDictionaryRef:
http://developer.apple.com/library/IOs/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/MultitouchEvents/MultitouchEvents.html#//apple_ref/doc/uid/TP40009541-CH3-SW7
(Scroll down to the section called "Handling Multitouch Events".)
Just tried your code, which have some problems.
I get "Began Began Began End End" for two fingers sometimes because touchesBegan get called two times and first time have one began touch second time have two began touches.
I don't know why you didn't split the method and put the code into the touchesBegan, touchesMoved, touchesEnded methods. But you should use touches that passed from the argument instead of [event allTouches].
- (void) handleTouches:(NSSet *)touches {
for( UITouch* touch in touches ) {
if( touch.phase == UITouchPhaseBegan ) {
NSLog(#"Began!");
}
else if( touch.phase == UITouchPhaseMoved ) {
}
else if( touch.phase == UITouchPhaseEnded || touch.phase == UITouchPhaseCancelled ) {
NSLog(#"Ended!");
}
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self handleTouches:touches];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[self handleTouches:touches];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self handleTouches:touches];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self handleTouches:touches];
}