In my view I'm overriding all the "touches*" methods to let the user draw on the screen. I'm recording the locations.
In addition I have two gesture recognizers on my view to detect single tap and double tap.
If I now move my finger just a little bit and short enough, I will be recording a small "draw" gesture. However when raising the finger, an additional tap gesture will be triggered.
By trial and error I could possibly figure out a minimum time and movement threshold but I'm sure there are smarter ways?
I need to know after how much movement and/or it is save to assume that no tap gesture will trigger.
You can avoid tap gestures. Instead of that you can recognize taps in touch events itself.
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
if(touches.count == 1)
{
if([[touches anyObject] tapCount] == 1)
{
// Do the action here for single tap
}
else if([[touches anyObject] tapCount] == 2)
{
// Do the action here for double tap
}
}
}
And you have to set a global bool variable for check whether user moved the finger on the screen.
BOOL _isMoved;
And make it TRUE in the touch move event
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
_isMoved = YES;
}
Before recording the track, you check whether this flag is TRUE or not? And also dont forget to make the flag to FALSE after saving the track
Hope this will help you :)
Related
On iPhone's with 3D touch enabled, there is a feature where long pressing the left hand side of the screen with enough force opens lets you change which app is active. Because of this, when a non-moving touch happens on the left hand side of the screen, the touch event is delayed for a second or two until the iPhone verifies that the user is not trying to switch tasks and is interacting with the app.
This is a major problem when developing a game with SpriteKit, as these touches are delayed by a second every time a user taps/holds their finger on the left edge of the screen. I was able to solve this problem by registering a UILongPressGestureRecognizer in the main Scene of the game, thus disabling TouchesBegan and implementing a custom touches function (used as a selector by the gesture recognizer):
-(void)handleLongPressGesture:(UITapGestureRecognizer *)gesture {
CGPoint location = [gesture locationInView:self.view];
if (gesture.state == UIGestureRecognizerStateBegan)
{
//
}
else if (gesture.state == UIGestureRecognizerStateChanged)
{
//
}
else if (gesture.state == UIGestureRecognizerStateEnded)
{
//
}
else if (gesture.state == UIGestureRecognizerStateCancelled)
{
//
}
}
-(void)didMoveToView:(SKView *)view {
/* Setup your scene here */
UILongPressGestureRecognizer *longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPressGesture:)];
longPressGestureRecognizer.delaysTouchesBegan = false;
longPressGestureRecognizer.minimumPressDuration = 0;
[view addGestureRecognizer:longPressGestureRecognizer];
// continue
}
The problem with this is that I would have to implement a gesture recognizer for every touch (including simultaneous ones) that I expect the user to enter. This interferes with any touchesBegan methods as subclasses of SKSpriteNode, SKScene, etc. and kills a lot of functionality.
Is there any way to disable this delay? When registering the gestureRecognizer, I was able to set delaysTouchesBegan property to false. Can I do the same somehow for my SKScene?
To see this issue in action, you can run the default SpriteKit project, and tap (hold for a second or two) near the left hand side of the screen. You will see that there is a delay between when you touch the screen and when the SKShapeNodes are rendered (as opposed to touching anywhere else on the screen).
* Edit 1 *
For those trying to find a way to get around this for now, you can keep the gesture recognizer but set its cancelsTouchesInView to false. Use the gesture recognizer to do everything you need to do until TouchesBegan kicks in (touchesBegan will receive the same touch event about a second after the gesture recognizer recognizes the touch). Once touchesBegan kicks in, you can disable everything happening in the gesture recognizer. This seems like a sloppy fix to me, but it works for now.
Still trying to find a more-or-less formal solution.
I have experienced this as an user and it is really annoying. The only thing that worked for me was to disable the 3D touch. Otherwise the left side of the touchscreen is almost useless.
The player drags a sprite in my game but when accidentally touch the screen with a second finger it screws the movement obviously.
I used the following solutions for disable the second touch, but unfortunately it doesn't work:
//--------------
-(void)touchesBegan:(NSSet*) touches withEvent:(UIEvent*) event {
if (touches.count == 1 && draggedNode == nil) {
CGPoint pos = [[touches anyObject] locationInNode:self];
SKNode * touchedNode = [self nodeAtPoint:pos];
if([touchedNode.name isEqual: #"shooterBall"]){
draggedNode = touchedNode;
}
draggedNodeOffset = CGPointMake(draggedNode.position.x - pos.x, draggedNode.position.y - pos.y);
}
}
//--------------
-(void)touchesMoved:(NSSet*) touches withEvent:(UIEvent*) event {
if (touches.count <= 1) {
CGPoint pos = [[touches anyObject] locationInNode:self];
draggedNode.position = CGPointMake(pos.x + draggedNodeOffset.x, pos.y+draggedNodeOffset.y);
}
}
//--------------
-(void)touchesEnded:(NSSet*) touches withEvent:(UIEvent*) event {
draggedNode = nil;
}
//--------------
Do you have any solution for this?
Thanks your help in advance!
You want to implement UIPanGestureRecognizer in your scene. It will allow you to track the location of the user's touch and at the same time control other "stray" touches: UIPanGestureRecognizer Documentation
After you initialize it, you need to implement a method to handle the user's pans. You will have to set flags inside of this method to control when the swipe started/ended. I think this answer on StackOverflow gave a really good explanation of using it (with Swift). BTW, when you initialize it, you should set the gesture recognizer's property maximumNumberOfTouches to 1 (that will cause it to ignore other touches while the user is panning).
The trickier part will be to translate the same code you wrote before to gesture recognizer. The difference is that your handler will be called only once for each "swipe" or "pan", while the touches method you are using now is called each time there is a "touch". There are a few ways to proceed at this point, and you could try whatever you like, but I think that this would be the easiest way to go once you have your gesture recognizer set up (spoiler):
make sure the gesture recognizer is an instance variable so you can access it from all methods.
go to the update: method and make an if statement that checks if gesture.state == UIGestureRecognizerStateBegan || gesture.state == UIGestureRecognizerStateChanged
use the same algorithm that you had before in this if statement. To check the location that the touch is at use the method: locationInView:. Use self.view as the parameter.
Hope this helped! good luck.
So I am working on an app for an iPhone and I need to detect if the player is holding the screen and I need to get the location of where they are pressing.
Originally I was using the touchesBegin function but that is only called when the screen is touched a new time. Is there a way that I can modify this to be called more often, like every time the the update function is called?
Another thing I looked into was the UILongGesture stuff, but I couldn't get the location of where it was pressed.
Any help/advice to figuring this out would be greatly appreciated.
I am being specific here on your question about how to get the location of where user tap,
- (void)handleTap:(UITapGestureRecognizer *)tapRecognizer
{
CGPoint touchPoint = [tapRecognizer locationInView: _tileMap] //locationInView is the method you need to try
}
Secondly, UIGestureRecognizer also gives you the state which may be useful in your case
UIGestureRecognizerStateBegan UIGestureRecognizerStateChanged
UIGestureRecognizerStateEnded
and so on.
Hope this helps
You were on the right track - are you using multitouch?
If not, all you need to do is set a variable when the user touches the screen, then clear that variable on the touches ended function. Using the update function would work but is a super inefficient way of doing it
I'm assuming you want something to happen when they keep their finger on the screen. You could schedule an event in say 2 seconds when they touch the screen, and then if they release the screen before that you just cancel the event.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInView(yourScrollView)
}
}
#IBAction func handlePan(recognizer:UIPanGestureRecognizer) {
let translation = recognizer.translationInView(self.view)
var currentLocation : CGPoint = CGPointMake(location.x+translation.x, location.y+translation.y)
recognizer.setTranslation(CGPointZero, inView: self.view)
}
This code finds the initial location of the touch, and then the new location whenever the touch is moved. However, no code will be called without using the touchesEnded method. I can't give you much more without knowing how you intend to use the touch.
I need two functions which would be fired when the rotation gesture starts and finishes, because I need to know the whole angle of the rotation. Currently the gesture recogniser is fired all the time until the rotation finishes, and I cannot find out when it has finished, to find to total angle.
That's because the method you hook to your gesture gets called for all of the gestures states, like began/ended/canceled/changed. You can however ask the gesture for its current state within the method, and add specific functionality for these different states. Here's a basic example:
- (void)rotationGestureHandler:(UIRotationGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateBegan) {
// do stuff - call method for gesture began
}else if (gesture.state == UIGestureRecognizerStateEnded) {
// do other stuff - call method for gesture ended
}
}
I have a view with four pan gestures attached. The first has both max and min number of touches set to 1, the second to 2, etc. This makes it so each will only recognize one touch while up to four fingers slide around on the screen.
That's working dandy. What isn't working is detecting when individual touches end. Anything I have set to happen when a gesture ends only happens when all gestures have ended completely.
Example delegate method:
- (void) handlePan:(UIPanGestureRecognizer*)recognizer {
//Setting what happens when a gesture is recognized as beginning
if (recognizer.state == UIGestureRecognizerStateBegan) {
//...whatever happens, bunnies follow your finger or whatever
} else
//Setting what happens when a gesture ends
if ((recognizer.state == UIGestureRecognizerStateEnded) |
(recognizer.state == UIGestureRecognizerStateCancelled) |
(recognizer.state == UIGestureRecognizerStateFailed)) {
NSLog(#"end");
}
}
What should be happening is that I see "end" in the console whenever any finger is lifted. Instead, I see nothing until all fingers are lifted, at which point I see "end" repeated four times (or as many times as fingers that were on the screen).
Is there any way I can make this work the way I intend?
edit After fiddling I see that I may not be analyzing my problem correctly. The whole reason I want to detect when a gesture's touch ends is that I want to have gestures able to become active when there is more than one touch on screen, but I want each gesture to only track one touch itself. I was setting an "active" flag on gestures that were tracking touches, and then toggling that flag off after touches ended, and that wasn't working, because touch-end-detection was hard to implement well.
But if there's a different way to achieve the same thing, that's the real thing I'm looking for: among many overlapping touches, have each gesture recognizer track one and only one.
You may want to do something like - it catches the change in fingers on the screen for the given gesture; you may need to add some more logic surrounding which gesture you're working with:
switch( recognizer.numberOfTouches ) {
case 1: {
NSLog(#"1 ");
break;
}
case 2: {
NSLog(#"2");
break;
}
case 3: {
NSLog(#"3");
break;
}
case 4: {
NSLog(#"4");
break;
}
default: {
NSLog(#"0");
}
}
This is what eventually worked.
In short, I made a flag that flipped whenever a gesture recognizer was assigned a touch, ensuring no other recognizers accepted that touch. I also tested each recognizer to make sure it only accepted a touch when it wasn't already following a touch. So I made each touch only get assigned once, and each recognizer only accept one touch. Worked like a charm.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//set this to no every time a new touch happens, meaning it isn't taken yet.
touchTaken = NO;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
//If the touch is taken or the gesture's already following a touch, say no.
if (touchTaken | ([gestureRecognizer numberOfTouches] > 0)) {
return NO;
}
else {
touchTaken = YES;
return YES;
}
}