I want to detect a touch while one finger is still on the screen means that still touch canceled for that is not called... is it possible because i want to ignore that touch and perform operation on the next finger......
Thanks
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//those are all touches on this began call
for (UITouch *touch in touches) {
CGPoint location = [touch locationInView: [touch view]];
CGPoint convertedLocation = [[CCDirector sharedDirector] convertToGL:location];
//those are ALL the touches on screen
NSSet *allTouches = [event allTouches];
You can also keep track of touches by reading the hash code of the touch and saving it using int firstTouchHash = [touch hash].
You can check in the TouchesBegan if the touches set and the allTouches set have the same count (in this case we are touching the screen for the first time), in that case we can save the hash int firstTouchHash = [touch hash]
When we touch the screen again, the allTouches contains more elements than touches and we always know wich was the first touch because we have the hash saved.
(without saving the hash, if we touch with 1 finger, then with a second one and then with a third, when the third touch occurs we dont know which of the 2 previous touches is the first, because they both are in the allTouches set (and a set is an unordered collection of elemenents) )
Also make sure multiple touch is enabled on the view, either through the multipleTouchEnabled property or the Multiple Touch checkbox in the storyboard editor.
Related
i followed this wonderful guide about mario-style game:
http://www.raywenderlich.com/62053/sprite-kit-tutorial-make-platform-game-like-super-mario-brothers-part-2
however, i wanted to convert the movement controls to arrow keys, implemented by SKSpriteNodes with names that are detected by:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode* node = [self nodeAtPoint:location];
// other left, up, down arrows with same code here
if ([node.name isEqualToString:#"rightArrow"]) {
self.player.moveRight = YES;
self.rightOriginalTouchLocation = location;
...
}
}
self.player.moveRight is a boolean value (much like moveForward in the guide), that tells the character at update to move.
it is terminated at:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode* node = [self nodeAtPoint:location];
// other left, up, down arrows with same code here
if ([node.name isEqualToString:#"rightArrow"]) {
self.player.moveRight = NO;
}
...
}
however, i encounter the following problem - when i start the touch on the arrow, drag it outside the arrow, and then release the tap, it is not recognized as 'touch ended' for the arrow node (and it doesn't stop moving because of that).
i tried to solve it in many ways (even calculating touch move distance from original location and see if its too far, then cancel movement), but i always manage to reproduce the constant motion problem.
the issue lies with the fact that i can tap two arrows at the same time, so it is not enough to remember the last node tapped.
since i want to allow movement for different directions at the same time, i cant stop all movements in case one button is dismissed. i need to specifically know which button was released so i can stop that direction's movement only.
do you have any ideas for me? should i implement it in another way considering i want the arrow keys, or is there a method to detect which node is released even though it is not at its original location (of the tap)?
Thank you very much!
if anyone is interested about the issue - i had a problem where touching and moving from SKSpriteNode didnt call the touchesEnded for that SKSpriteNode (since the 'release' of the touch was not in the node).
i solved it by keeping the CGPoint of the touch at touchesBegan, and when touchesEnded called, i calculated distances to nearest key using simple distance function:
-(int)calculateDistanceWithPoints:(CGPoint)point1 andPoint:(CGPoint)point2 {
float d = sqrtf(pow((point1.x - point2.x), 2) + pow((point1.y - point2.y), 2));
return (int)d;
}
and then, in touchesEnded, i checked to which key the distance is minimal(i have 3 keys, so which key is most likely to be the key that was 'released') - and did the action required for that key release.
I want to shift the position of some UILabels when the user swipes the screen--specifically, move them the same distance the user swiped their finger.
How would I go about detecting and implementing this?
Thanks
Override touchesBegan:withEvent and touchesMoved:withEvent:. Keep track of the starting location in touchesBegan:withEvent. If repeated calls to touchesDragged (it is called while the drag is happening) show that you are moving fast enough for the action to be a swipe versus just a drag, use the difference between the starting location and the current touch location to animate changing the UILabels.
You can do that by using following methods for UIView.
In touchesBegan you should store the position where the user first touches the screen. So you can identify the left or right swipe when touches ends.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
self.startPoint = [touch locationInView:self];
}
Record the final position on touchesEnded method. By comparing this two positions you can determine the direction of movement.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint endPosition = [touch locationInView:self];
if (startPoint.x < endPoint.x) {
// Right swipe
} else {
// Left swipe
}
}
I hope this helps.
I have the following issue:
If i tap the touch screen with one finger (i.e. middle finger) and close to the moment that the finger is going up i tap with a second finger (i.e. index finger) close to the area where the other finger was, instead of getting a down event, i get a move of the first finger to
the down position of the second finger. It is a little tricky to reproduce but it is reproducible.
I have a UIView and for the touches i have the following:
- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event
{
for (UITouch *touch in touches) {
int index = (int)touch;
CGPoint position = [touch locationInView:self];
NSLog(#"DOWN %d x:%f y:%f", index, position.x, position.y);
}
}
- (void) touchesMoved: (NSSet *) touches withEvent: (UIEvent *) event
{
for (UITouch *touch in touches) {
int index = (int)touch;
CGPoint position = [touch locationInView:self];
NSLog(#"MOVE %d x:%f y:%f", index, position.x, position.y);
}
}
- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event
{
for (UITouch *touch in touches) {
int index = (int)touch;
CGPoint position = [touch locationInView:self];
NSLog(#"UP %d %f %f", index, position.x, position.y);
}
}
When i reproduce it i have the following output
DOWN 510365696 x:891.500000 y:197.000000
MOVE 510365696 x:774.000000 y:263.000000
MOVE 510365696 x:766.000000 y:268.500000
UP 510365696 764.500000 270.000000
where the above MOVE is the down position of the second finger. As you can also see the UP from the first finger which should be in pos (891,197) is totally lost...
I know that i could detect these "jumps" but there is no way to differentiate them from a fast moving finger.
I appreciate any help.
When I tried with your same code, what I observed is that, even the slightest movement in your finger is detected.
When you tap using your first finger and while holding it, if you tap using the second finger, the second finger position is not logged. Actually, what gets logged is the slight variation in point of your first finger which is observed as a move action.
The up position which gets logged is also somewhere near to the first finger touch point.
The down action of the second finger is never detected.
I think the problem is that Multiple Touch option is not enabled.
With your view selected, in the Utilities pane, select Attributes Inspector and from there tick the Multiple Touch checkbox.
Here is a screen shot :
The actual Log when Multiple Touch is enabled will look like this:
DOWN 475396224 x:229.000000 y:6.500000
DOWN 475411840 x:102.000000 y:50.000000
MOVE 475396224 x:231.500000 y:8.000000
MOVE 475411840 x:104.500000 y:51.500000
UP 475396224 231.500000 8.000000
UP 475411840 104.500000 51.500000
Hope this helps !
I'm trying to create a program in which when I tap in roughly the same location as where my last touch ended, a circle changes color. I've tried using the previousLocationInView method using logic along the lines of: "if touchesbegan location == previousTouchesLocation, change color to blue." This method didn't work because the touchesBegan and previousTouchLocation coordinates have the same value which always changed the color whenever I tapped the screen regardless of the location. I get the same result if I substitute previousTouchLocation with touchesEnded. Here is the code if anyone thinks that it could help.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
touchLocation = [touch locationInView:self.view];
lastTouchLocation = [[touches anyObject] previousLocationInView:self.view];
distance_x = touchLocation.x - cvx;
distance_y = cvy - touchLocation.y;
previousDistance_x = lastTouchLocation.x - cvx;
previousDistance_y = cvy - lastTouchLocation.y;
if ((previousDistance_x == distance_x) && (previousDistance_y == distance_y)) {
if (([cv.color isEqual:[UIColor redColor]])) {
[cv setColor:[UIColor blueColor]];
}
else
[cv setColor:[UIColor redColor]];
}
}
-previousLocationInView: gives you the previous location of a touch that's currently moving on the screen. It doesn't give you the location of a different, now ended, touch.
What you're going to have to do is, on the first touch, store the touch's location in a property or ivar. Then, on the second touch, compare its location to the stored location. You'll need to allow for some leeway—fingers are not pixel-accurate in the way mice can be—so a touch that's moved ten or twenty pixels should probably count as a double-tap.
Actually, come to think of it, your best bet might be to use a UITapGestureRecognizer with numberOfTapsRequired set to 2. This will handle all the detection logic for you, and you can combine it with other gesture recognizers to build up more complex behaviors.
I have a virtual keyboard in my app with 6 keys, and the whole thing is just an image implemented with UIImageView. I determined the exact x and y coordinates that correspond to the image of each 'key' and used the following code to respond to a user interacting with the keyboard:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch * touch = [[event allTouches] anyObject];
CGPoint point = [touch locationInView:touch.view];
if(point.y < 333 && point.y > 166 && point.x < 72 && point.x > 65)
{
NSLog(#"Key pressed");
}
//Repeat per key...
}
However, I have realized that this method is not very smart, because changing phone perspectives (portrait to landscape) or changing devices will ruin my x and y coordinates and therefore cause problems.
So, I am looking for an alternative to specifying the absolute x and y values, and using touchesMoved in general. Ideally, it would be a button with specific settings that would call its method if it was tapped, or if the user dragged their finger into the area of the button (even if very slowly - I used swipe detection before and it required too much of an exaggerated movement).
Is it possible to set up a button to call its method if tapped or if a touch event started outside of the button and then proceded into the button? If not, what are my alternatives?
Thanks SE!
You need to get the winSize property, which will fix the problem you are having with screen sizes.
CGSize size = [[CCDirector sharedDirector]winSize];
I do believe you are using Cocos2D? If so you can use this size property instead of hard coding numbers. :)
to convert your point, use
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector]convertToGL:location];
To see if its within the bounds/box of the button you could try:
if (CGRectContainsPoint ([self.myButton boundingBox], location)
{
//Execute method
}
This is all assuming you are using Cocos2D and a CCSprite for your button.
This should work on any screen size and portrait or landscape :)