Building your own gesture recognizer from touches methods? - ios

I'm having an issue with swipe gesture recognizer - it is sometimes slow and requires some distance to detect. For example sometime when you move your finger just a little it won't detect.
I'm making a twitch game and handle my input with gestures in all four directions and this behavior leads to all sorts of confusion and irritates players.
So I figured I need to implement my own swipe recognizer. Or maybe there is a library done by someone else?
My thinking so far is to store a location of touch in touchesBegan: method and then check for new locations in touchesMoved: or touchesEnded: methods. Then I will compare the distance and direction and fire correct methods.
Is this a correct way to do this or am I missing something?

It is a correct way but far from easy actually. I created a class that does something like that for me (for throwing a view actually).
What I do in this class is generate a point buffer and a date-time buffer which I can then use to compute things like speed, gesture length... On the outside it looks like this:
#property NSInteger maximumBufferSize;
#property (readonly) CGFloat traceLength;
#property (readonly) CGPoint traceSpeed;
- (void)begin:(CGPoint)point;
- (void)push:(CGPoint)point;
- (void)end:(CGPoint)point;
Pretty convenient to use. In your case begin, push and end are touches began, touches moved and touches ended (or canceled). Maximum buffer size is the number of points it will store (N last points received by touches that is).
You must understand you will probably need a bit more then that if you want to ignore none swipe gestures (user draws a circle with his finger for instance). To do such things you might want to compute an average way of the trace and then compare each sub-trace (point[i+1] - point[i]) so that dot(trace.averageWay, sub-trace.averageWay) = 1+-trashold.
Edit: added source link
Source link

UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipe:)];
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipe:)];
// Setting the swipe direction.
[swipeLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
[swipeRight setDirection:UISwipeGestureRecognizerDirectionRight];
// Adding the swipe gesture on image view
[self.imageView addGestureRecognizer:swipeLeft];
[self.imageView addGestureRecognizer:swipeRight];
- (void)handleSwipe:(UISwipeGestureRecognizer *)swipe {
if (swipe.direction == UISwipeGestureRecognizerDirectionLeft) {
}
if (swipe.direction == UISwipeGestureRecognizerDirectionRight) {
}
}

You can read this tutorial
[http://www.raywenderlich.com/44270/sprite-kit-tutorial-how-to-drag-and-drop-sprites]
I make sure that you will find the solutions for your problems.

Related

How does UILongPressRecognizer work?

If you start pressing the screen, but move the finger because minimumPressDuration ellapses, the gesture gets cancelled and your movement gets forwared to the view. If minimumPressDuration is reached, it doesn't matter how much you move the finger. I want to avoid this and always cancel my gesture if the finger movement is bigger than allowableMovement.
I've seen this thread, but that solution isn't working for me.
I've tried subclassing UILongPressureGestureRecognizer, and set the state to failed or cancelled when my requirement is met, but doesn't seem to work, I guess cancelling it isn't enough and have to forward the events myself? How should I do this? My intention is to use it together with MKMapView. I'm getting really frustrated with this, I've tried it for two days.
Try this one :
UILongPressGestureRecognizer *longpress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(LongPress_action:)];
longpress.minimumPressDuration = 2.0; //seconds
longpress.delegate = self;
[yourview addGestureRecognizer:longpress]; // where you want to add
Call this method :
- (void)LongPress_action:(UILongPressGestureRecognizer*)gesture
{
if ( gesture.state == UIGestureRecognizerStateEnded )
{
NSLog(#"Long Press");
}
}

how do you make flappy bird flap?

So I was just trying to make a replica of flappy bird. But, I was wondering how to achieve a bird flap? I thought of using the method
- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event
but I noticed that if you use this method and press down on the screen, the bird will flap continuously. I just want the bird to flap when I tap the screen, and if I press down on the screen it will behave the same way as tapping once.
Is there a class reference that utilizes these sort of action?
For getting the tap event, you can use the UITapGestureRecognizer provided by Apple.
Simply initiate it, set the parameters according to your requirements and add it to the view you want to record the tap.
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
tap.numberOfTapsRequired = 1;
tap.numberOfTouchesRequired = 1;
[demoView addGestureRecognizer:tap]; // The gesture recognizer is being added to demoView
[tap release]; // In case of not using ARC
And the define the selector which you mentioned in the initialization.
- (void)handleTap:(UITapGestureRecognizer *)recognizer{
// Handle the tapping here
}
I´m very frustrated because I can´t draw the Ground like in Flappy Bird... I try to use this method:
private void drawGround(){
for(Rectangle mRectangleGroundHelper : mArrayGround){
if(spawnGround & mRectangleGroundHelper.x<0){ // spawn Ground if the actual ground.x + ground.width() is smaller then the display width.
mArrayGround.add(mRectangleGroundHelper);
spawnGround = false;
}
}
for(Rectangle mRectangleGroundHelper : mArrayGround){
if(mRectangleGroundHelper.x < -mTextureGround.getWidth()){ // set boolean to true, if the actual ground.x completely hide from display, so a new ground can be spawn
spawnGround = true;
}
}
for(Rectangle mRectangleGroundHelper : mArrayGround){ // move the ground in negative x position and draw him...
mRectangleGroundHelper.x-=2;
mStage.getSpriteBatch().draw(mTextureGround, mRectangleGroundHelper.x, mRectangleGroundHelper.y);
}
}
You can download app at here

Is there a built in way to add a gesture recognizer (specifically pan) just to the right or left edges of a view?

Throughout iOS 7 there's many situations where users can slide their finger in from the left or right edge of the screen in order to perform an action, such as popping a view controller or showing a sidebar.
Is there a built in way to do this that I've completely overlooked somehow (yes, I've searched extensively)? Or is the only way to check the frame position of where the pan started?
This is because I want to perform distinct actions if the user pulls from the edge, or say the middle.
You have UIScreenEdgePanGestureRecognizer, which is added to iOS7 to detect, well, panning from the edges of the screen. For panning from the middle, a normal pan gesture recognizer will suffice, where you can check if the pan gesture originated close enough to the middle.
Use the UIScreenEdgePanGestureRecognizer, but check that it's available first (since it's iOS 7+):
if (NSClassFromString(#"UIScreenEdgePanGestureRecognizer")) {
UIScreenEdgePanGestureRecognizer *panRecognizer =
[[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self
action:#selector(handleScreenEdgePanGesture:)];
panRecognizer.edges = UIRectEdgeLeft;
}
Set up gestures
UIPanGestureRecognizer* panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanning:)];
[self.view addGestureRecognizer:panGesture];
Handling gesture states
- (void)handlePanning:(UIPanGestureRecognizer *)gestureRecognizer
{
switch ([gestureRecognizer state])
{
case UIGestureRecognizerStateBegan:
[self startDragging:gestureRecognizer];
break;
//u won't need following cases
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateFailed:
[self stopDragging:gestureRecognizer];
break;
default:
break;
}
}
Recognizing start point of drag
- (void)startDragging:(UIPanGestureRecognizer *)gestureRecognizer
{
CGPoint pointInSrc = [gestureRecognizer locationInView:yourVIEW];
}
The pan gesture recognizer is a predefined recognizer, about all you can do with it is to determine how fast the user moved their finger. If you want to tell if where the movement started, you'll have to code your own recognizer. It's not that difficult, you'll be notified when the touch started and where it ended.

UIPanGestureRecognizer in SKScene

I've been experimenting with UIGestureRecognizers and the new SKScene/SKNode's in SpriteKit. I've had one problem, and I got close to fixing it but I am confused on one thing. Essentially, I have a pan gesture recognizer that allows the user to drag a sprite on the screen.
The single problem I have is that it takes ONE tap to actually initialize the pan gesture, and then only on the SECOND tap on it works correctly. I'm thinking that this is because my pan gesture is initialized in touchesBegan. However, I don't know where else to put it since initializing it in the SKScene's initWithSize method stopped the gesture recognizer from actually working.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (!self.pan) {
self.pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(dragPlayer:)];
self.pan.minimumNumberOfTouches = 1;
self.pan.delegate = self;
[self.view addGestureRecognizer:self.pan];
}
}
-(void)dragPlayer: (UIPanGestureRecognizer *)gesture {
CGPoint trans = [gesture translationInView:self.view];
SKAction *moveAction = [SKAction moveByX:trans.x y:-trans.y duration:0];
[self.player runAction:move];
[gesture setTranslation:CGPointMake(0, 0) inView:self.view];
}
That's because you're adding the gesture in touches began, so the gesture doesn't exist until the screen has been tapped at least once. Additionally, I would verify that you're actually using initWithSize: as your initializer, because you shouldn't have any problems adding the gesture there.
Another option is to move the code to add the gesture into -[SKScene didMovetoView:] which gets called immediately after the scene has been presented. More info in the docs.
- (void)didMoveToView:(SKView *)view
{
[super didMoveToView:view];
// add gesture here!
}
This is my first post! Hoping to not trip over my own toes...
Hi guys, so I was having an issue with a UISwipeGestureRecognizer not working. I was initializing it in my initWithSize method so based on this post I moved it to my didMoveToView method. Now it works (thanks 0x7fffffff). All I did was cut the following two lines from one method and paste them in the other.
_warpGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(warpToNextLevel:)];
[self.view addGestureRecognizer:_warpGesture];
In my "investigation" I came across userInteractionEnabled and tried to set it to YES in my initWithSize method...
self.view.userInteractionEnabled = YES;
NSLog(#"User interaction enabled %s", self.view.userInteractionEnabled ? "Yes" : "No");
This would log NO even though i'd just set it to YES. Further investigation found that if I don't try to manually set userInteractionEnabled then it's NO during initWithSize (I can't seem to change this if I want to) and automatically gets set to YES when i'm in didMoveToView.
This all strikes me as relevant but I would love for someone in the know to explain just what's going on here. Thanks!

With UIPanGestureRecognizer, is there a way to only act so often, like after x many pixels were panned?

Right now my UIPanGestureRecognizer recognizes every single pan, which is great and necessary, but as I'm using it as a sliding gesture to increase and decrease a variable's value, within the method I only want to act every so often. If I increment by even 1 every time it's detected the value goes up far too fast.
Is there a way to do something like, every 10 pixels of panning do this, or something similar?
You're looking for translationInView:, which tells you how far the pan has progressed and can be tested against your minimum distance. This solution doesn't cover the case where you go back and forth in one direction in an amount equal to the minimum distance, but if that's important for your scenario it's not too hard to add.
#define kMinimumPanDistance 100.0f
UIPanGestureRecognizer *recognizer;
CGPoint lastRecognizedInterval;
- (void)viewDidLoad {
[super viewDidLoad];
recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(didRecognizePan:)];
[self.view addGestureRecognizer:recognizer];
}
- (void)didRecognizePan:(UIPanGestureRecognizer*)sender {
CGPoint thisInterval = [recognizer translationInView:self.view];
if (abs(lastRecognizedInterval.x - thisInterval.x) > kMinimumPanDistance ||
abs(lastRecognizedInterval.y - thisInterval.y) > kMinimumPanDistance) {
lastRecognizedInterval = thisInterval;
// you would add your method call here
}
}

Resources