Detecting 2 swipes without interrupting touch - iphone

I am trying to implement game control that will be changes direction of unit
movement. So if i do right swipe it turns to the right, if i do down swipe it turns to the down direction etc.
It's cocos2d game, and i'am using CCNode+SFGestureRecognizers with UISwipeGestureRecognizer.
At now I have next implementation for it
UISwipeGestureRecognizer *rightSwipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleRightSwipe:)];
[self addGestureRecognizer:rightSwipeGestureRecognizer];
rightSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
rightSwipeGestureRecognizer.delegate = self;
[rightSwipeGestureRecognizer release];
UISwipeGestureRecognizer *upSwipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleUpSwipe:)];
[self addGestureRecognizer:upSwipeGestureRecognizer];
upSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionUp;
upSwipeGestureRecognizer.delegate = self;
[upSwipeGestureRecognizer release];
UISwipeGestureRecognizer *leftSwipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleLeftSwipe:)];
[self addGestureRecognizer:leftSwipeGestureRecognizer];
leftSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
leftSwipeGestureRecognizer.delegate = self;
[leftSwipeGestureRecognizer release];
UISwipeGestureRecognizer *downSwipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleDownSwipe:)];
[self addGestureRecognizer:downSwipeGestureRecognizer];
downSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionDown;
downSwipeGestureRecognizer.delegate = self;
[downSwipeGestureRecognizer release];
but the problem is that for to recognize the following gesture you need to lift your finger from the screen. if you do not lift a finger after the first swipe, it will recognize only the first swipe.
at now:
How it should be:
What the best way to do that? Thanks!

cancelsTouchesInView should help, it prevents the cancellation of touch events when a gesture recognizer has recognized a gesture. This will allow other gesture recognizers to continue checking for their gesture.
rightSwipeGestureRecognizer.cancelsTouchesInView = NO;
upSwipeGestureRecognizer.cancelsTouchesInView = NO;
leftSwipeGestureRecognizer.cancelsTouchesInView = NO;
downSwipeGestureRecognizer.cancelsTouchesInView = NO;

Ok, there is my solution. I dont think that is really good solution, but in my case it works, because i need getting direction at every time interval.
So, in my CCLayer subclass:
#define SWIPE_LENGTH 60
#define SWIPE_TANGENT 2
...
#property(nonatomic, assign) CGPoint latestLocation;
...
-(id) init
{
if( (self=[super init]) )
{
self.isTouchEnabled = YES;
}
return self;
}
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
self.latestLocation = location;
}
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
CGPoint delataLocation = CGPointMake(location.x - self.latestLocation.x, location.y - self.latestLocation.y);
if ( sqrt(pow((delataLocation.x), 2) + pow((delataLocation.y), 2) ) > SWIPE_LENGTH ) {
if (delataLocation.y > 0 && delataLocation.y > SWIPE_TANGENT * ABS(delataLocation.x))
{
[self handleSwipeWithDirestion:SDirectionTypeUp];
} else if (delataLocation.y < 0 && ABS(delataLocation.y) > SWIPE_TANGENT * ABS(delataLocation.x))
{
[self handleSwipeWithDirestion:SDirectionTypeDown];
} else if (delataLocation.x > 0 && delataLocation.x > SWIPE_TANGENT * ABS(delataLocation.y))
{
[self handleSwipeWithDirestion:SDirectionTypeRight];
} else if (delataLocation.x < 0 && ABS(delataLocation.x) > SWIPE_TANGENT * ABS(delataLocation.y))
{
[self handleSwipeWithDirestion:SDirectionTypeLeft];
}
self.latestLocation = location;
}
}

Related

How to detect in touchesEnded which nodes are still being pressed

I've been stuck on this problem for days. What I have is multiple SKSpriteNode's, one for a left arrow, right arrow and up arrow. When I hold down the right arrow I want my character to continue moving right while its being held down, on the other hand if you press the up Arrow then you will only jump once regardless of if you hold it down.
So my problem for example is when i hold the right arrow and then i press the up arrow, touchesEnded is called and it stops my character from moving right even though I still have my finger on the right arrow
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
for (UITouch *touch in touches){
CGPoint location = [touch locationInNode:self];
if (CGRectContainsPoint(rightArrow.frame, location)){
[wizard setTexture:[SKTexture textureWithImageNamed:#"wizardRight"]];
didTouchRightArrow = YES;
isLookingRight = YES;
isLookingLeft = NO;
rightArrow.alpha = 0.5;
NSLog(#"Touching right");
}
if (CGRectContainsPoint(leftArrow.frame, location)){
[wizard setTexture:[SKTexture textureWithImageNamed:#"wizardLeft"]];
isLookingRight = NO;
isLookingLeft = YES;
didTouchLeftArrow = YES;
leftArrow.alpha = 0.5;
NSLog(#"Touching left");
}
if (CGRectContainsPoint(upArrow.frame, location)){
didTouchUpArrow = YES;
upArrow.alpha = 0.5;
NSLog(#"Touching up");
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
if (rightArrow.alpha != 1.0){
rightArrow.alpha = 1.0;
}
if (leftArrow.alpha != 1.0){
leftArrow.alpha = 1.0;
}
if (upArrow.alpha != 1.0){
upArrow.alpha = 1.0;
for (UITouch *touch in touches){
CGPoint location = [touch locationInNode:self];
if (CGRectContainsPoint(rightArrow.frame, location)){
NSLog(#"Touching right");
didTouchRightArrow = YES;
} {
NSLog(#"Not touching right");
didTouchRightArrow = NO;
}
if (CGRectContainsPoint(leftArrow.frame, location)){
NSLog(#"Touching Left");
didTouchLeftArrow = YES;
} else {
NSLog(#"not touching left");
didTouchLeftArrow = NO;
}
didTouchUpArrow = NO;
}
This may not be the right way to approach the problem, but in touchesEnded I am trying to see if the touch is still in the desired Rect.
You need a way to identify the different nodes which are registering a touch. There is more than one way to do this but I have always found using the name property of a node to be the simplest and easiest to work with.
You already have the right idea by using the BOOLs to register the touch states.
I wrote some code to handle what you are trying to accomplish:
#import "GameScene.h"
#implementation GameScene {
SKSpriteNode *node0;
SKSpriteNode *node1;
BOOL node0touch;
BOOL node1touch;
}
-(void)didMoveToView:(SKView *)view {
self.backgroundColor = [SKColor blackColor];
node0 = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(100, 100)];
node0.name = #"node0";
node0.position = CGPointMake(100, 300);
[self addChild:node0];
node1 = [SKSpriteNode spriteNodeWithColor:[SKColor blueColor] size:CGSizeMake(100, 100)];
node1.name = #"node1";
node1.position = CGPointMake(400, 300);
[self addChild:node1];
node0touch = false;
node1touch = false;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:touchLocation];
if([node.name isEqualToString:#"node0"])
node0touch = true;
if([node.name isEqualToString:#"node1"])
node1touch = true;
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:touchLocation];
if([node.name isEqualToString:#"node0"])
node0touch = false;
if([node.name isEqualToString:#"node1"])
node1touch = false;
}
}
-(void)update:(CFTimeInterval)currentTime {
if(node0touch)
NSLog(#"node0 touch");
if(node1touch)
NSLog(#"node1 touch");
}

Selecting images and then moving them

I am trying to figure out how to select an object first then I am able to move it. For example I want to select cow first by touching it then I am able to move it. The reason being is when I am touching the screen it is moving both cow and cow1. When I only want to move one cow at a time. Any help will be appreciated.
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:touch.view];
cow.center = CGPointMake(touchLocation.x, touchLocation.y);
cow1.center = CGPointMake(touchLocation.x, touchLocation.y);
}
Try using UIPanGestureRecognizer, like this:
#synthesize myImage; //UIImageView
- (void)viewDidLoad
{
[super viewDidLoad];
[self startMoveImage];
}
-(void) startMoveImage{
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(pan:)];
[self.view addGestureRecognizer:panGestureRecognizer];
}
- (void)pan:(UIPanGestureRecognizer *)gesture
{
if ((gesture.state == UIGestureRecognizerStateChanged) ||
(gesture.state == UIGestureRecognizerStateEnded)) {
CGPoint position = [gesture locationInView:[myImage superview]];
[myImage setCenter:position];
}
}
There are many ways to implement this sort of thing. It depends on how your graphics are implemented, amongst other things, check out this Apple sample code:
http://developer.apple.com/library/ios/#samplecode/Touches/Introduction/Intro.html
The standard solution would be to add separate gesture recognizers for your two cow objects, e.g., in viewDidLoad:
UIPanGestureRecognizer *panCow1 = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(moveCow:)];
[cow1 addGestureRecognizer:panCow1];
// if non ARC, make sure to add a line that says
// [panCow1 release];
UIPanGestureRecognizer *panCow2 = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(moveCow:)];
[cow2 addGestureRecognizer:panCow2];
// if non ARC, make sure to add a line that says
// [panCow2 release];
And then you have your moveCow method look something like:
- (void)moveCow:(UIPanGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateChanged)
{
CGPoint translate = [sender translationInView:self.view];
sender.view.center = CGPointMake(sender.view.center.x + translate.x, sender.view.center.y + translate.y);
}
}
I haven't tested this code, but you get the idea. This is how I generally move separate subviews around...

NavCtrl + TableViewCtrl + ScrollView, TableView not getting touches

I've got a NavCtrl and on top of that I have a TableView/ctrl, then I have a scrollview.
I've got everything working, except I can't seem to get touches to hit the Cell's on the table view.
I've tried the nextResponder things and everything seems to be doing the right thing. Here's some snippets of code, keep in mind this code has been changed a lot for testing so it isn't "clean" yet :):
ScrollView *myScrollView = [[ScrollView alloc] initWithFrame:CGRectMake(0, 150, 320, 680)];
myScrollView.contentSize = CGSizeMake( 320 , 888);
myScrollView.pagingEnabled = NO;
myScrollView.bounces = NO;
myScrollView.directionalLockEnabled = YES;
myScrollView.canCancelContentTouches = NO;
myScrollView.delaysContentTouches = NO;
myScrollView.scrollEnabled = true;
myScrollView.userInteractionEnabled = YES;
myScrollView.scrollsToTop = NO;
mySettingsTableView = [[SettingsTableView alloc] init];
// Settings View Ctrl
SettingsTableViewCtrl *mySettingsTableViewCtrl = [[SettingsTableViewCtrl alloc] initWithStyle:UITableViewStyleGrouped];
mySettingsTableViewCtrl.tableView.scrollEnabled = NO;
mySettingsTableViewCtrl.tableView.delaysContentTouches = NO;
mySettingsTableViewCtrl.tableView.canCancelContentTouches = NO;
mySettingsTableViewCtrl.view.userInteractionEnabled = YES;
[mySettingsTableViewCtrl.view addSubview:myScrollView];
[mySettingsTableViewCtrl.view addSubview:mySettingsTableView];
* In subclass of scrollView *
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//if (![self yourMethodThatDeterminesInterestingTouches:touches withEvent:event])
[super touchesBegan:touches withEvent:event];
[self.nextResponder touchesBegan:touches withEvent:event];
}
Here's the NSLog from the touch events:
NSLog(#"Event: %#", event);
NSLog(#"Event NextResponder : %#\n", self.nextResponder);
2011-07-05 13:09:36.063 App[1924:14f03] Event: <UITouchesEvent: 0x802ea30> timestamp: 8503.71 touches: {(
<UITouch: 0x80e6f60> phase: Began tap count: 1 window: <UIWindow: 0x836eb00; frame = (0 0; 320 480); layer = <CALayer: 0x836ebb0>> view: <ScrollView: 0x83770b0; baseClass = UIScrollView; frame = (0 150; 320 680); clipsToBounds = YES; layer = <CALayer: 0x8376e00>; contentOffset: {0, 0}> location in window: {140, 249} previous location in window: {140, 249} location in view: {140, 35} previous location in view: {140, 35}
)}
2011-07-05 13:09:36.065 App[1924:14f03] Event NextResponder : <SettingsTableViewCtrl: 0x8377db0>
I have the same problem with my code, and solved it by adding UITapGestureRecognizer to the view to which I want to handle touches.
In your case you should also add a UITapGestureRecognizer to either scroll view if you want to handle touches on it or table view if you want to handle touches on it as;
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tappedScrollView)];
[tapRecognizer setNumberOfTapsRequired:1];
[myScrollView addGestureRecognizer:tapRecognizer];
[tapRecognizer release];
and what you want to do on touch should be done in a method as;
-(void)tappedScrollView
{
//TO-DO what you want after Scroll view touched, DO it here.
}
Similarly you can do it for your table view as;
UITapGestureRecognizer *tapRecognizerForTableView = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tappedTableView)];
[tapRecognizerForTableView setNumberOfTapsRequired:1];
[mySettingsTableView addGestureRecognizer:tapRecognizerForTableView];
[tapRecognizerForTableView release];
and what you want to do on touch should be done in a method as;
-(void)tappedTableView
{
//TO-DO what you want after Table view touched, DO it here.
}
Here's what I ended up, not perfect yet but working, any tuning advice would be much appreciated......
//AppDelegate.m
ScrollView *myScrollView = [[ScrollView alloc] initWithFrame:CGRectMake(0, 140, 320, 680)];
myScrollView.contentSize = CGSizeMake( 320 , 888);
myScrollView.pagingEnabled = NO;
myScrollView.bounces = NO;
myScrollView.directionalLockEnabled = YES;
myScrollView.canCancelContentTouches = NO;
myScrollView.delaysContentTouches = NO;
myScrollView.scrollEnabled = true;
myScrollView.userInteractionEnabled = YES;
myScrollView.scrollsToTop = NO;
mySettingsTableViewCtrl = [[SettingsTableViewCtrl alloc] initWithStyle:UITableViewStyleGrouped];
mySettingsTableViewCtrl.tableView.scrollEnabled = NO;
mySettingsTableViewCtrl.tableView.delaysContentTouches = NO;
mySettingsTableViewCtrl.tableView.canCancelContentTouches = NO;
mySettingsTableViewCtrl.view.userInteractionEnabled = YES;
[mySettingsTableViewCtrl.view addSubview:myScrollView];
[mySettingsTableViewCtrl.view addSubview:mySettingsTableView];
//ScrollView.m
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
[self.nextResponder touchesBegan:touches withEvent:event];
}
//SettingsTableView.m
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
NSUInteger tapCount = [touch tapCount];
CGPoint location = [touch locationInView:self];
NSIndexPath *indexPathAtHitPoint = [self indexPathForRowAtPoint:location];
if ( [self cellForRowAtIndexPath:indexPathAtHitPoint] ) {
[self.delegate tableView:self didSelectRowAtIndexPath:indexPathAtHitPoint];
} else {
[super touchesBegan:touches withEvent:event];
}
}
Now I have a DatePicker on top of the scrollview and its working but this code needs some additional code to manage it, i.e. detect when picker is "touched" and when its not there. But that shouldn't be too difficult (I think ;)
Thanks for help and comments,
D
I think it might be overkill to use a ScrollView for your purpose. You can "scroll" any view. Position it so part or all of it is out of bounds, set clipping mode on the parent view, and when you want to "scroll" it, change its frame to in-bounds, with animation in effect. That is, if you are scrolling the DatePicker in in response to some other event (like adding an item to the table that is associated with a date). If the user scrolls the date picker in by dragging then carry on.
Combining a scroll view and a table view will be tricky, since a table view is a scroll view. It's ambiguous which should handle scrolling gestures. It might be possible but you probably have to write extra code to make the ambiguity explicit.

Detect Double tap in UIScrollView

How can I detect double tap in UIScrollview?
I tried:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
if (touch.tapCount == 2) {
// some operations here
}
}
But this is not detecting double tap in UIScrollView, is there any other way to detect double tap?
Regards
If you're targeting 3.2+ you could try using a UITapGestureRecognizer
UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] initWithTarget:yourViewController action:#selector(tapGesture:)];
tap.numberOfTapsRequired = 2;
tap.numberOfTouchesRequired = 1;
[scrollView addGestureRecognizer:tap];
[tap release];
Then you handle the tap in your viewcontroller:
- (void)tapGesture:(UIGestureRecognizer*)gesture {
// Do something
}
Use the below code for double tap.
-(void) viewDidLoad{
// other necessary operations
UITapGestureRecognizer* dTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : #selector (doubleTap:)];
[doubleTap setDelaysTouchesBegan : YES];
dTap.numberOfTapsRequired = 2;
dTap.numberOfTouchesRequired = 1;
[self.view addGestureRecognizer : dTap];
[dTap release];
}
- (void) doubleTap : (UIGestureRecognizer*) sender
{
NSLog (#"Double tap Do operations here...");
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSUInteger numTaps = [[touches anyObject] tapCount];
if (numTaps== 2) {
//some operations here
}
}
Use this Code

How to implement zoom-in/zoom-out effect on a cocos2d sprite image?

I am developing module where-in I pick the image from photo library and put into a sprite. I want to implement zoom-in/zoom-out kind of effect for a sprite image, same like camera album images zoom in/out effect. Could someone please guide me how do i implement it?
I see somewhere is that, I have to detect two touch events in ccTouchBegan and then adjust the sprite's scale size to up or down based on the distance of two fingers touch event values.
Could someone please tell me:
How do i detect two fingers touch values in ccTouchBegan?
How to allow to touch and zoom-in/out of sprite image by user? Please give me samples. I tried already some stuff from this URL, but doesn't work for my requirement.
Thank you.
It would be simpler to use gesture recognizer for zooming:
// on initializing your scene:
UIPinchGestureRecognizer* PinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget: self action: #selector (zoom:)];
[[[Director sharedDirector] openGLView] addGestureRecognizer: PinchGesture];
...
/// zoom callback:
-(void) zoom: (UIPinchGestureRecognizer*) gestureRecognizer
{
if (([gestureRecognizer state] == UIGestureRecognizerStateBegan) || ([gestureRecognizer state] == UIGestureRecognizerStateChanged))
yourSprite.scale = [gestureRecognizer scale];
}
1) First step you need create two variables.
BOOL canPinch;
float oldScale;
2) Use the brigadir's :) answer and add this in your init method
UIPinchGestureRecognizer* pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action: #selector (zoom:)];
[[[CCDirector sharedDirector] openGLView] addGestureRecognizer:pinchGesture];
3)
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSArray* allTouches = [[event allTouches] allObjects];
UITouch* touch = [touches anyObject];
CGPoint location = [self convertTouchToNodeSpace:touch];;
CGRect particularSpriteRect = CGRectMake(spriteToZoom.position.x-[spriteToZoom boundingBox].size.width/2, spriteToZoom.position.y-[spriteToZoom boundingBox].size.height/2, [spriteToZoom boundingBox].size.width, [spriteToZoom boundingBox].size.height);
if(CGRectContainsPoint(particularSpriteRect, location))
{
if ([allTouches count] == 2)
{
canPinch = YES;
return;
}
else if ([allTouches count] == 1)
{
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
CGPoint oldTouchLocation = [touch previousLocationInView:touch.view];
oldTouchLocation = [[CCDirector sharedDirector] convertToGL:oldTouchLocation];
oldTouchLocation = [self convertToNodeSpace:oldTouchLocation];
CGPoint translation = ccpSub(touchLocation, oldTouchLocation);
[self panForTranslation:translation];
}
}
canPinch = NO;
}
- (void)panForTranslation:(CGPoint)translation
{
CGPoint newPos = ccpAdd(spriteToZoom.position, translation);
spriteToZoom.position = newPos;
}
- (void)zoom: (UIPinchGestureRecognizer*) gestureRecognizer
{
if (canPinch)
{
if (([gestureRecognizer state] == UIGestureRecognizerStateBegan) || ([gestureRecognizer state] == UIGestureRecognizerStateChanged))
{
spriteToZoom.scale = oldScale + [gestureRecognizer scale]-(oldScale != 0 ? 1 : 0);
}
if ([gestureRecognizer state] == UIGestureRecognizerStateEnded)
{
oldScale = spriteToZoom.scale;
}
}
}