I have read a bunch of questions on this but none of them seem to achieve what I am looking for... So lets say I have an arbitrary UIWebView inside of a UIViewController. The UIViewController has a SwipeGestureRecognizer that works fine. It even works within the UIWebView-- whenever there is no scrollbar. (Before I load a page or even if I load a page that can fit appropriately within the size of my UIWebView). However, if I load a webpage that requires horizontal scrolling left or right, then inside the UIWebView part of my view, I cannot get any swipe gestures to be recognized. Every click/drag/swipe just triggers the scroll action. Is there a way to differentiate between a "swipe" and just scrolling with your finger (not lifting it but rather dragging to scroll).
Yes, you can tell the UIWebView's UIScrollView that its UIPanGestureRecognizer should only fire when your own UISwipeGestureRecognizer has failed.
This is how you do it:
UISwipeGestureRecognizer *rightSwipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeGesture:)];
UISwipeGestureRecognizer *leftSwipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeGesture:)];
rightSwipeGesture.direction = UISwipeGestureRecognizerDirectionRight;
leftSwipeGesture.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:rightSwipeGesture];
[self.view addGestureRecognizer:leftSwipeGesture];
[_webView.scrollView.panGestureRecognizer requireGestureRecognizerToFail:rightSwipeGesture];
[_webView.scrollView.panGestureRecognizer requireGestureRecognizerToFail:leftSwipeGesture];
That should do the trick for you.
You will have to subclass UIWebView and override the gesture recogniser calls.
EDIT - Look at this post Handling touches inside UIWebview and this link http://mithin.in/2009/08/26/detecting-taps-and-events-on-uiwebview-the-right-way/
Johannes Fahrenkrug's answer succeeded in conditionally blocking the webView's built-in pan gestures. However, I found that this only worked if the webView's pan gestures were very slow ... if I panned the webView with any reasonable speed, the swipe gesture was triggered. I wanted only a fast swipe to trigger the swipe gesture, and medium or slow pans to use the default webView scrolling functionality.
The UISwipeGestureRecognizer has no properties for customizing the speed of a swipe, and the UIPanGestureRecognizer has a velocity property but no way to set a required velocity, so I set up a custom gesture recognizer based on this tutorial:
FastSwipeGestureRecognizer.h
#import <UIKit/UIKit.h>
#import <UIKit/UIGestureRecognizerSubclass.h>
#define REQUIRED_TOUCHES 5
#define REQUIRED_STRAIGHTNESS 3
#define REQUIRED_TIME .1
typedef enum {
DirectionUp = 0,
DirectionRight,
DirectionDown,
DirectionLeft
} Direction;
#interface FastSwipeGestureRecognizer : UIGestureRecognizer {
CGPoint firstTouchLocation;
NSTimeInterval firstTouchTime;
int touchesCount;
Direction direction;
}
#property (nonatomic) CGPoint firstTouchLocation;
#property (nonatomic) NSTimeInterval firstTouchTime;
#property (nonatomic) int touchesCount;
#property (nonatomic) Direction direction;
#end
FastSwipeGestureRecognizer.m
#import "FastSwipeGestureRecognizer.h"
#implementation FastSwipeGestureRecognizer
#synthesize firstTouchLocation;
#synthesize firstTouchTime;
#synthesize touchesCount;
-(void)reset {
[super reset];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
self.firstTouchLocation = [[touches anyObject] locationInView:self.view];
self.firstTouchTime = [NSDate timeIntervalSinceReferenceDate];
self.touchesCount = 1;
self.state = UIGestureRecognizerStatePossible;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
self.touchesCount++;
if (self.touchesCount > REQUIRED_TOUCHES) { // wait until we have a few touches before we evaluate the gesture
CGPoint thisTouchLocation = [[touches anyObject] locationInView:self.view];
float horizontalRatio = (ABS(thisTouchLocation.x - self.firstTouchLocation.x) / ABS(thisTouchLocation.y - self.firstTouchLocation.y));
float verticalRatio = 1 / horizontalRatio;
NSTimeInterval elapsedTime = [NSDate timeIntervalSinceReferenceDate] - self.firstTouchTime;
NSLog(#"swipe? %f, %f, %f", verticalRatio, horizontalRatio, elapsedTime);
// if we're moving straight enough and fast enough, complete the gesture
if (((horizontalRatio > REQUIRED_STRAIGHTNESS)||(verticalRatio > REQUIRED_STRAIGHTNESS))&&(elapsedTime < REQUIRED_TIME)) {
if (horizontalRatio > REQUIRED_STRAIGHTNESS) {
self.direction = (thisTouchLocation.x > self.firstTouchLocation.x) ? DirectionRight : DirectionLeft ;
} else if (verticalRatio > REQUIRED_STRAIGHTNESS) {
self.direction = (thisTouchLocation.y > self.firstTouchLocation.y) ? DirectionDown : DirectionUp ;
}
self.state = UIGestureRecognizerStateRecognized;
} else {
self.state = UIGestureRecognizerStateFailed;
}
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
if (self.touchesCount < REQUIRED_TOUCHES) {
self.state = UIGestureRecognizerStateFailed;
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesCancelled:touches withEvent:event];
self.state = UIGestureRecognizerStateFailed;
}
#end
Set up your gestures
FastSwipeGestureRecognizer *swipeGesture = [[FastSwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeGesture:)];
[self.view addGestureRecognizer:swipeGesture];
[self.webView.scrollView.panGestureRecognizer requireGestureRecognizerToFail:swipeGesture];
Then detect the direction of the received gesture
- (void)handleSwipeGesture:(FastSwipeGestureRecognizer *)gesture {
if (gesture.state == UIGestureRecognizerStateEnded) {
if (gesture.direction == DirectionRight) {
// do something
} else if (gesture.direction == DirectionLeft) {
// do something
} else if (gesture.direction == DirectionUp) {
// do something
} else if (gesture.direction == DirectionDown) {
// do something
}
}
}
Note that this only requires one gesture to handle all four swipe directions, instead of one UISwipeGestureRecognizer per direction.
Related
I am sure that this problem will be easily resolved however I am relatively new to iOS development. I am trying to handle passing touch events to children that are lower in the draw order on a UIView. For example -
I create extended UIImageView to create my MoveableImage class. This class is just basically UIImageView that implements the touchesBegan,touchesEnded and touchesMoved-
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self showFrame];
//if multitouch dont move
if([[event allTouches]count] > 1)
{
return;
}
UITouch *touch = [[event touchesForView:self] anyObject ];
// Animate the first touch
CGPoint colorPoint = [touch locationInView:self];
CGPoint touchPoint = [touch locationInView:self.superview];
//if color is alpha of 0 , they are touching the frame and bubble to next responder
UIColor *color = [self colorOfPoint:colorPoint];
[color getRed:NULL green:NULL blue:NULL alpha:&touchBeganAlpha];
NSLog(#"alpha : %f",touchBeganAlpha);
if(touchBeganAlpha > 0)
{
[self animateFirstTouchAtPoint:touchPoint];
}
else {
[super.nextResponder touchesBegan:touches withEvent:event];
}
}
So the end result is basically this- If they are touching the frame of the imageView and not the image inside the other image that will be underneath can possibly respond. See this image for an example.
So far I have tried next responder however that does not solve the problem. Any help would be greatly appreciated!
Resolved- I stopped checking the alpha on the touchesBegan and touchesMoved. Ovveriding pointInside allowed the UIView to handle that for me.
-(BOOL) pointInside:(CGPoint)point withEvent:(UIEvent *) event
{
BOOL superResult = [super pointInside:point withEvent:event];
if(!superResult)
{
return superResult;
}
if(CGPointEqualToPoint(point, self.previousTouchPoint))
{
return self.previousTouchHitTestResponse;
}else{
self.previousTouchPoint = point;
}
BOOL response = NO;
//if image is nil then return yes and fall back to super
if(self.image == nil)
{
response = YES;
}
response = [self isAlphaVisibleAtPoint:point];
self.previousTouchHitTestResponse = response;
return response;
}
You can override instead - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event method for your subclass of UIImageView. (It is a method of uiview that each subclass can override)
The UIView uses this method in hitTest:withEvent: to determine which subview should receive a touch event. If pointInside:withEvent: returns YES, then the subview’s hierarchy is traversed; otherwise, its branch of the view hierarchy is ignored.
Check the source code on github of OBShapedButton. They handle tap event only for the opaque part of a button.
In my code I have an array of objects which are UIImageViews with UIImages in them.
I use a for loop in -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event to move all objects from the array one by one.
Once they are moved to different positions on the main view I would like to be able to grab any of those objects and move them somewhere else.
I'm not sure how best to do this. Is there a way I can use touchesbegin so that I can drag only the item I clicked on?
Basically, there are five UIImageViews there now and each one has UIImage and I am trying to get to the point where when i click on any of them they are able to move around.
EDIT: John Muchow posted this on a blog here. You should be able to adapt for your own purposes.
//UIDraggableImageView.h
#import <UIKit/UIKit.h>
#interface UIDraggableImageView : UIImageView
{
CGPoint currentPoint;
}
#end
//UIDraggableImageView.m
#import "UIDraggableImageView.h"
#implementation UIDraggableImageView
- (id)initWithImage:(UIImage *)image
{
if (self = [super initWithImage:image]) {
self.userInteractionEnabled = YES;
}
return self;
}
-(void)dealloc {
[super dealloc];
}
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
// When a touch starts, get the current location in the view
currentPoint = [[touches anyObject] locationInView:self];
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
// Get active location upon move
CGPoint activePoint = [[touches anyObject] locationInView:self];
// Determine new point based on where the touch is now located
CGPoint newPoint = CGPointMake(self.center.x + (activePoint.x - currentPoint.x),
self.center.y + (activePoint.y - currentPoint.y));
//--------------------------------------------------------
// Make sure we stay within the bounds of the parent view
//--------------------------------------------------------
float midPointX = CGRectGetMidX(self.bounds);
// If too far right...
if (newPoint.x > self.superview.bounds.size.width - midPointX)
newPoint.x = self.superview.bounds.size.width - midPointX;
else if (newPoint.x < midPointX) // If too far left...
newPoint.x = midPointX;
float midPointY = CGRectGetMidY(self.bounds);
// If too far down...
if (newPoint.y > self.superview.bounds.size.height - midPointY)
newPoint.y = self.superview.bounds.size.height - midPointY;
else if (newPoint.y < midPointY) // If too far up...
newPoint.y = midPointY;
// Set new center location
self.center = newPoint;
}
#end
I've done something similar with great success using the UIPanGestureRecognizer in my draggable view. To hook it up, it might look something like this:
#implementation DraggableView
-(id)initWithFrame:(CGRect)frame
{
...
...
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(pan:)] autorelease];
[self addGestureRecognizer:panGesture];
..
return self;
}
#end
In your pan: method, you could check the state of the gesture and even capture the gesture's location and use it to relocate your view. Here's a rough example of what it might look like:
-(void)pan:(UIPanGestureRecognizer *)gesture
{
if (gesture.state==UIGestureRecognizerStateChanged)
{
//We're moving!
UIView *aRootView = <perhaps your root view controller's view>;
CGPoint currentOrigin = [gesture translationInView:aRootView];
self.frame = CGRectMake(currentOrigin.x,currentOrigin.y,self.frame.size.width,self.frame.size.hight);
...
...
}
}
Here is more reading on the UIPanGestureRecognizer.
You might want to consider adding gesture recognizers to the views that you want to move, that way you can easily tell when they are tapped and you can use touchesMoved to move them around...
On a button click add an image from camera roll to the imageview. Image can zoom in and out and should drag that image to any place in the view.
If you have a normal UITextView with loads of text in it you can swipe up and down:
if you swipe left or right in a very horizontal line, nothing will happen
if you swipe left or right with a very slight angle, you will swipe up or down
So there is obviously some kind of class definition which tells the UITextView when to react to a swipe and when not to. Something like a maximumVariance constant, I guess.
My problem is that I'm trying to subclass an UITextView to enable left/right swipe. However, you will need to swipe in a pretty straight line, otherwise, you'll get an up or down swipe. So my question is if there is someway to re-define the standard variance variable of an UITextView?
Here is my subclass .m file. Note that the kMaximumVariance doesn't really have any effect as this is overwritten by whatever variance the standard UITextView class provides:
//
// SwipeTextView.m
// Swipes
//
#import "SwipeTextView.h"
#import "PageView.h"
#define kMinimumGestureLength 15
#define kMaximumVariance 100
#implementation SwipeTextView
#synthesize delegate, gestureStartPoint;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
UITouch *touch =[touches anyObject];
gestureStartPoint = [touch locationInView:self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
[self resignFirstResponder]; // this hides keyboard if horizonal swipe
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self];
CGFloat deltaXX = (gestureStartPoint.x - currentPosition.x); // positive = left, negative = right
CGFloat deltaYY = (gestureStartPoint.y - currentPosition.y); // positive = up, negative = down
CGFloat deltaX = fabsf(gestureStartPoint.x - currentPosition.x); // will always be positive
CGFloat deltaY = fabsf(gestureStartPoint.y - currentPosition.y); // will always be positive
NSLog(#"deltaXX (%.1f) deltaYY (%.1f) deltaX (%.1f) deltaY (%.1f)",deltaXX, deltaYY, deltaX, deltaY);
if (deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance) {
if (deltaXX > 0) {
NSLog(#"Horizontal Left swipe - FINISHED detected");
}
else {
NSLog(#"Horizontal Right swipe - FINISHED detected");
}
}
}
#end
EDIT:
Suggestion to cancel touches reaching UITextView (see below):
// Prevent recognizing touches
- (BOOL)gestureRecognizer:(UISwipeGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if ([gestureRecognizer direction] == UISwipeGestureRecognizerDirectionLeft) {
// Left swipe: I want to handle this
return YES;
}
if ([gestureRecognizer direction] == UISwipeGestureRecognizerDirectionRight) {
// Right swipe: I want to handle this
return YES;
}
if ([gestureRecognizer direction] == UISwipeGestureRecognizerDirectionUp) {
// Up swipe: textView needs to handle this
return NO;
}
if ([gestureRecognizer direction] == UISwipeGestureRecognizerDirectionDown) {
// Down swipe: textView needs to handle this
return NO;
} else
return YES;
}
-(void)viewDidLoad
{
UITapGestureRecognizer *GesRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doSomething:)];
[GesRec setCancelsTouchesInView:NO];
[self addGestureRecognizer:GesRec];
[GesRec release];
}
-(void)doSomething
{
NSLog(#"doSomething");
}
Try adding an UISwipeGestureRecognizer to the view and set cancelsTouchesInView to false so the swipe gesture is not passed through. This should let you detect left and right motion more accurately.
If this doesn't do the trick, implement your own subclass of UIGestureRecognizer and add that instead.
Edit
You add the gesture recognizer to the UITextView in your controller's viewDidLoad method.
In your delegate methods, you can't return NO in gestureRecognizer:shouldReceiveTouch: because it means the gesture recognizer won't examine the incoming touch object. In your case you want to return YES when the swipe is horizontal and NO otherwise, so vertical swipes are passed through while horizontal swipe is completed handled by you.
No subclass needed. set textView.userInteractionEnabled to NO and than put a transparent UIView over your textView. handle touches and gestures anyway you want on that UIView and make the textView respond to these programatically.
how to remove subviews very fast according to user touched point or moving point in the view.I am able to get the user touched point and moved point in the view and also i can remove the subview according with the user touched point using [subview removefromsuperview];.
When the user moves very fast some subviews getting deleted and some of the images not getting deleted.If the user moves slowly then subviews getting deleted exactly.i am placing some of my code is below
`
-(void)deleteSubView:(CGPoint)userCurrentPoint{
for(int i=0;i<[subviews count];i++){
if (CGRectContainsPoint([[subviews objectAtIndex:i] CGRectValue], userCurrentPoint)) {
[[superview viewWithTag:i ] removeFromSuperview];
}
}
`
is there any way to delete subviews according with the user fast moving points.i need your suggestion.
Thanks all
Put this in your header file:
#interface YourView : UIView
#property (nonatomic, assign) CGPoint lastTouchLocation;
#end
This should be the implementation:
#implementation YourView
#synthesize lastTouchLocation;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
self.lastTouchLocation = [[touches anyObject] locationInView:self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
CGPoint oldLocation = self.lastTouchLocation;
CGPoint newLocation = [[touches anyObject] locationInView:self];
for (UIView *subview in self.subviews)
{
if (LineIntersectsRect(oldLocation, newLocation, subview.frame))
{
[subview removeFromSuperview];
}
}
self.lastTouchLocation = newLocation;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
self.lastTouchLocation = CGPointZero;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
self.lastTouchLocation = CGPointZero;
}
#end
What this does: we check whether or not a line between the last received touch and the current one intersects the frame of a subview. Why? See deanWombourne's answer.
Here's the intersection code, originally by habjan - you should put in in the header, too.
static inline BOOL LineIntersectsLine(CGPoint l1p1, CGPoint l1p2, CGPoint l2p1, CGPoint l2p2)
{
CGFloat q = (l1p1.y - l2p1.y) * (l2p2.x - l2p1.x) - (l1p1.x - l2p1.x) * (l2p2.y - l2p1.y);
CGFloat d = (l1p2.x - l1p1.x) * (l2p2.y - l2p1.y) - (l1p2.y - l1p1.y) * (l2p2.x - l2p1.x);
if(d == 0)
{
return false;
}
CGFloat r = (q / d);
q = (l1p1.y - l2p1.y) * (l1p2.x - l1p1.x) - (l1p1.x - l2p1.x) * (l1p2.y - l1p1.y);
CGFloat s = (q / d);
if((r < 0) || (r > 1) || (s < 0) || (s > 1))
{
return false;
}
else
{
return true;
}
}
static inline BOOL LineIntersectsRect(CGPoint p1, CGPoint p2, CGRect r)
{
if (CGRectContainsPoint(r, p1) || CGRectContainsPoint(r, p2))
{
return YES;
}
else
{
CGPoint topLeft = CGPointMake(r.origin.x, r.origin.y);
CGPoint topRight = CGPointMake(r.origin.x + r.size.width, r.origin.y);
CGPoint bottomLeft = CGPointMake(r.origin.x, r.origin.y + r.size.height);
CGPoint bottomRight = CGPointMake(r.origin.x + r.size.width, r.origin.y + r.size.height);
return (LineIntersectsLine(p1, p2, topLeft, topRight) ||
LineIntersectsLine(p1, p2, topRight, bottomRight) ||
LineIntersectsLine(p1, p2, bottomRight, bottomLeft) ||
LineIntersectsLine(p1, p2, bottomLeft, topLeft));
}
}
It's not to do with your enumeration :)
If the user moves their finger quickly, you don't get a list of every pixel they touched, you get some of them. If they were to move fast enough, you might only get the start and end points.
You can't just use the point that their finger is dragged to, you have to work out which subviews have been touched between the last event and this one :)
Think of it as drawing a line between the start and end points and working out which subviews overlap with that line.
If I understand your question correctly, you want to remove all the subviews that the user touches as he drags his finger across your view.
Your problem is that touchesMoved:withEvent: fires at a limited frequency. It isn't guaranteed to fire for every pixel the user touches. As a result, when the user moves his finger quickly, there are gaps between the points reported in touchesMoved:withEvent. If the user moves his finger quickly enough, these gaps may be large enough that they skip over some subviews entirely.
To solve this problem your code needs to remember the position of the previous point, and then test the line segment formed by the previous point and the current point to see if it intersects with the frame of the subview. An efficient algorithm for this is described in: How to test if a line segment intersects an axis-aligned rectange in 2D?
I am trying to detect double taps on a view, but when the double tap comes, the first tap triggers an action on TouchesBegan, so, before detecting a double tap a single tap is always detected first.
How can I make this in a way that just detects the double tap?
I cannot use OS 3.x gestures, because I have to make it compatible with old OS versions.
thanks
Some excerpts from the tapZoom example of the scrollViewSuite sample code:
First, the function to kick off things once the touch ended:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ([touch tapCount] == 1) {
[self performSelector: #selector(handleSingleTap)
withObject: nil
afterDelay: 0.35]; // after 0.35s we call it a single tap
} else if([touch tapCount] == 2) {
[self handleDoubleTap];
}
}
Second: intercept the message if a new touch occurs during timeout:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[NSObject cancelPreviousPerformRequestsWithTarget: self
selector: #selector(handleSingleTap)
object: nil];
}
see also: http://developer.apple.com/iphone/library/documentation/WindowsViews/Conceptual/UIScrollView_pg/ZoomingByTouch/ZoomingByTouch.html#//apple_ref/doc/uid/TP40008179-CH4-SW1
and here: (scrollView suite)
http://developer.apple.com/iphone/library/samplecode/ScrollViewSuite/Introduction/Intro.html
Edit: I missed the point that you said you cannot use 3.x gestures, so this is an invalid answer to your question, but I'm leaving it in case someone who can use 3.x gestures may benefit from it.
You can create two gesture recognizers, one for single tap and one for double tap:
UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTouchesOne:)];
singleTapGesture.cancelsTouchesInView = NO;
singleTapGesture.delaysTouchesEnded = NO;
singleTapGesture.numberOfTouchesRequired = 1; // One finger single tap
singleTapGesture.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:singleTapGesture];
[singleTapGesture release];
UITapGestureRecognizer *doubleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTouchesTwo:)];
doubleTapGesture.cancelsTouchesInView = NO;
doubleTapGesture.delaysTouchesEnded = NO;
doubleTapGesture.numberOfTouchesRequired = 1; // One finger double tap
doubleTapGesture.numberOfTapsRequired = 2;
[self.view addGestureRecognizer:doubleTapGesture];
[doubleTapGesture release];
And then, here comes the punch:
[singleTapGesture requireGestureRecognizerToFail : doubleTapGesture];
The last line, makes your single tap handler work only if the double tap fails. So, you get both single tap and double tap in your application.
Are you looking at the tapCount? For example:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
if (touch.tapCount == 2) {
//double-tap action here
}
}