How to disable second touch in an SpriteKit game? - ios

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.

Related

Press And Hold iPhone Screen

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.

Periodic Event While Pan Gesture Hasn't Ended

So my current project has a pan gesture recognizer, and if I have panned to the top of the screen the screen it is supposed to scroll up to account for that gesture. While the gesture hasn't ended and the current position of the gesture remains at the top of the screen, I want to continually keep scrolling. My problem is the gesture recognizer only gets called when the state changes, therefore my content will only scroll if you move back and forth at the top, not continually while the gesture continues to be at the top. Is there any reasonable way to continually call code while the gesture hasn't ended, but isn't necessarily changing? Here is pseudo-code of what I have:
- (void)handleGestureRecognizer:(UIGestureRecognizer *)gesture {
if ( gesture.state == UIGestureRecognizerStateChanged ) {
CGPoint point = [gesture locationInView:self.view];
if (point.y < 100) {
//I would like this code to be called continually while the
//gesture hasn't ended, not necessarily only when it changes
[self updateScrollPosition];
}
}
I can think of a few ghetto ways to do it by setting state bools based on the current state of the recognizer and creating my own timer to periodically check, but it seems pretty hacky and I don't particularly like it, so I'm wondering if anyone could come up with a cleaner solution.
One way to use a timer and feel better about it would be to conceal it a little by using performSelector:withObject:afterDelay:
- (void)stillGesturing {
[self updateScrollPosition];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self performSelector:#selector(stillGesturing) withObject:nil afterDelay:0.5];
}
// then in the recognizer target
if (gesture.state == UIGestureRecognizerStateEnded) {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
} else if ( gesture.state == UIGestureRecognizerStateChanged ) {
CGPoint point = [gesture locationInView:self.view];
if (point.y < 100) {
//I would like this code to be called continually while the
//gesture hasn't ended, not necessarily only when it changes
[self stillGesturing];
}
}

Possible to convert UIButton control events to UIGestureRecognizer?

I have a situation where I have to work around a bug in iOS7 and need to convert the UIButton control events:
UIControlEventTouchUpOutside
UIControlEventTouchDown
UIControlEventTouchUpInside
UIControlEventTouchDragOutside
UIControlEventTouchDragInside
UIControlEventTouchDragInside
to UIGestureRecgonizer (I can't use UIView touch methods such as touchesBegan, touchesEnded, etc. either). I'm kind of interested if this is even possible anyway.
For instance, I'm thinking of a way to convert UIControlEventTouchDown and can't think of a way. UITapGestureRecognizer and UIPanGestureRecognizer both would not work.
Anyone know if this is possible?
I've done something similar with a UILongPressGestureRecognizer for a SpriteKit game to implement line segment intersection, pretty sure it would be easily adaptable for mimicking a UIButton too. Keep track of the previous location of your gesture recognizer, I just used a static CGPoint that I updated every time my selector fired. Then, just check the previous position and the current position with CGRectContainsPoint using your button's frame, and depending on those results, do whatever you need to do. If both are inside the frame, that's the same as UIControlEventDragInside, if both are outside, that's UIControlEventDragOutside, if the previous is outside and the current is inside, that's UIControlEventDragEnter, etc etc. Also make sure to check the gesture recognizer's state so you know when to call TouchUpInside/Outside. I'd make sure to have a damn good reason to do this, but it seems workable to me.
- (void) longPress:(UILongPressGestureRecognizer *)longPressGestureRecognizer
{
if (longPressGestureRecognizer.state == UIGestureRecognizerStateBegan || longPressGestureRecognizer.state == UIGestureRecognizerStateChanged)
{
CGPoint touchedPoint = [longPressGestureRecognizer locationInView: self];
if (CGRectContainsPoint(self.bounds, touchedPoint))
{
[self addHighlights];
}
else
{
[self removeHighlights];
}
}
else if (longPressGestureRecognizer.state == UIGestureRecognizerStateEnded)
{
if (self.highlightView.superview)
{
[self removeHighlights];
}
CGPoint touchedPoint = [longPressGestureRecognizer locationInView: self];
if (CGRectContainsPoint(self.bounds, touchedPoint))
{
if ([self.delegate respondsToSelector:#selector(buttonViewDidTouchUpInside:)])
{
[self.delegate buttonViewDidTouchUpInside:self];
}
}
}
}

Is it possible to know when [UIDynamicItemBehavior addLinearVelocity:forItem:] has finished running?

I'm using a UIPanGestureRecognizer and UIAttachmentBehavior to move a UIView around the screen. When the user ends the gesture I apply the velocity of the gesture recognizer to the view using a UIDynamicItemBehavior and the addLinearVelocity:forItem: method.
Here is the code I use:
- (void)_handlePanGestureRecognized: (UIPanGestureRecognizer *)panGestureRecognizer
{
if (panGestureRecognizer.state == UIGestureRecognizerStateBegan)
{
_attachmentBehavior.anchorPoint = panGestureRecognizer.view.center;
[_dynamicAnimator addBehavior: _attachmentBehavior];
}
else if (panGestureRecognizer.state == UIGestureRecognizerStateChanged)
{
CGPoint point = [panGestureRecognizer locationInView: panGestureRecognizer.view.superview];
_attachmentBehavior.anchorPoint = point;
}
else if (panGestureRecognizer.state == UIGestureRecognizerStateEnded)
{
[_dynamicAnimator removeBehavior: _attachmentBehavior];
CGPoint velocity = [panGestureRecognizer velocityInView: panGestureRecognizer.view.superview];
[_dynamicItemBehavior addLinearVelocity: velocity
forItem: self];
}
}
When the view stops moving I would then like to have it snap to the closest edge of the screen but I currently have no way of knowing when it has stopped moving short of polling the view's center with a CADisplayLink.
Have you tried attaching a UIDynamicAnimatorDelegate to your animator, and using the dynamicAnimatorDidPause: method to trigger snapping to the closest edge?
From reading on the developer forums, it sounds like some have had problems with their views staying in motion for a very long time (jiggling back and forth by 1 pixel, for example), but perhaps this will work for your case.

How to prefer tap gesture over draw gesture?

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 :)

Resources