Why is gestureRecognizerShouldBegin called multiple times? - ios

I made gesture like this:
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(tapOnPhoto:)];
tapGesture.numberOfTapsRequired = 1;
tapGesture.numberOfTouchesRequired = 1;
tapGesture.delegate = self;
[self.htmlWebView addGestureRecognizer:tapGesture];
And when tap on htmlWebView call gestureRecognizerShouldBegin for several time.
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]){
[self performSelector:#selector(tapOnPhoto:) withObject:gestureRecognizer];
}
return YES;
}
Why is gestureRecognizerShouldBegin called more than once per gesture?

As per the
gestureRecognizerShouldBegin description.
This method is called when a gesture recognizer attempts to transition
out of the UIGestureRecognizerStatePossible state. Returning NO causes
the gesture recognizer to transition to the
UIGestureRecognizerStateFailed state.
This method will get called multiple times to get the transition state of gesture.
Also as you have added a selector for TapGesture why are you calling that method explicitly, the method will get called itself.
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]){
[self performSelector:#selector(tapOnPhoto:) withObject:gestureRecognizer];
}
return YES;
}
There will be no need of this function you can remove it as you are calling only a method there.

As #Rajat mentioned in his answer you can remove the delegate method and instead use your tapOnPhoto: method to parse your gesture logic.
The UIGestureRecognizer object that you'll receive as the argument into that method has a property called state
Which might have the following values:
UIGestureRecognizerStatePossible
UIGestureRecognizerStateBegan
UIGestureRecognizerStateChanged
UIGestureRecognizerStateEnded
UIGestureRecognizerStateCancelled
UIGestureRecognizerStateFailed
UIGestureRecognizerStateRecognized
You can use a switchand provide specific logic for each case.

Related

How to get a CGPoint from a tapped location on a custom View When Voice Over Enabled?

I am setting up my app to be compatible with Voice Over feature. I have a graph, I am getting single tap on the -accessibilitiHint(). I need to confirm the user action on double tap which the even can be obtained with accessibilityActivate(). The touch point needs to be retrieved on Single tap.
Now Is there any method to to get user touch point when single tap is performed on my View ?
For Objective C
Step 1:Add TapeGesture Recognizer to your View
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGestureHandler:)];
tapGesture.delegate = self;
[yourView addGestureRecognizer:tapGesture];
Step 2: implement Gesture Delegate Method
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
Step 3: implement Gesture Handler
- (void)tapGestureHandler:(UITapGestureRecognizer *)gesture
{
CGPoint touchPoint = [gesture locationInView:yourView];
}

IBAction not fired sometimes after UIGestureRecognizer handler

I'm trying to track hits on UI elements (tap and long press) using UIGestureRecognizer. After hit was tracked (let's say logged via NSLog) UI element should do it's job.
I'm creating gesture recognizers like this:
UITapGestureRecognizer* tap = [[UITapGestureRecognizer] alloc initWithTarget:self action:(OnGesture:)]
tap.cancelsTouchesInView = NO;
tap.delegate = self;
[view addGestureRecognizer:tap];
UILongPressGestureRecognizer* longPress = [[UILongPressGestureRecognizer] alloc initWithTarget:self action:(OnGesture:)]
longPress.cancelsTouchesInView = NO;
longPress.delegate = self;
[view addGestureRecognizer:longPress];
I've overridden some gesture recognizer methods:
-(BOOL)gestureRecognizer:(UIGestureRecognizer*)_recognizer shouldReceiveTouch(UITouch*)_touch
{
return YES;
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer*)_recognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)_otherRecognizer
{
return YES;
}
Inside the gesture recognizer handler, I'm trying to find the exact subview of the tap by using the hitTest method.
-(void)OnGesture:(UIGestureRecognizer*)_recognizer
{
if([_recognizer.state == UIGestureRecognizerStateEnded])
{
if([_recognizer isKindOfClass:[UITapGestureRecognizer class]]
|| [_recognizer isKindOfClass:[UILongPressGestureRecognizer class])
{
CGPoint location = [_recognizer locationOfTouch:0 inView:_recognizer.view];
// my problem occurs here:
//---------------------------------------------------------------------------
UIView* hitView = [_recognizer.view hitTest:location withEvent:nil];
//---------------------------------------------------------------------------
NSLog(#"Hit on view: %#", hitView);
}
}
}
So my problem is:
Sometimes (1 out of 10 cases) when I press the UIButton OnGesture method fires, but the IBAction of the "Touch Up Inside" event of that button is not firing.
But when I comment out hitTest call:
//UIView* hitView = [_recognizer.view hitTest:location withEvent:nil];
the bug stops being reproducible. IBAction always gets called.
Why is this happening? How can I fix this?
P.S. there could be some typos in the sample code above.
According to the docs, in order for it to work:
This method ignores view objects that are hidden, that have disabled user interactions, or have an alpha level less than 0.01. This method does not take the view’s content into account when determining a hit. Thus, a view can still be returned even if the specified point is in a transparent portion of that view’s content.
So you might wanna do self.someSubview.userInteractionEnabled = YES;

tableView is hiding due to a gestureRecognizer before it can execute didSelectRowAtIndexPath

I am trying to handle tableViewCell's being tapped, but the problem is that this is a "temporary tableView". I have it coded so that it will appear while the user is editing a UITextField, but then I set up a gesture recognizer to set the tableview to hidden as soon as the user clicks somewhere away from the UITextField.
I have the gesture recognizer set up as follows:
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(dismissKeyboard)];
[tap setCancelsTouchesInView:NO];
[self.view addGestureRecognizer:tap];
However, dismissKeyboard is called before didSelectRowAtIndexPath is called, and so the TableView that I want to handle the event on becomes hidden and therefore this function is never called.
My question is: Does anybody have ideas of how to get around this, so that didSelectRowAtIndexPath will execute before the tableView hides? I had one idea to somehow see if the tableView is where the tap is coming from, and if so, then don't execute the "hide tableView" line within dismissKeyboard. Is this possible?
Sorry, but I am new to iOS dev, so thank you for any advice!
You should be able to do this by making your view controller the tap gesture's delegate and denying it any touches that are inside the table view. Here is a starting point:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gesture shouldReceiveTouch:(UITouch *)touch
{
//Assuming your table view is a direct subview of the gesture recognizer's view
BOOL isInsideTableView = CGRectContainsPoint(tableView.frame, [touch locationInView:gesture.view])
if (isInsideTableView)
return NO;
return YES;
}
Hope this helps!
You could set yourself as a delegate to the UITapGestureRecognizer and cancel the gesture when the user taps within the tableView.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
//You can also (and should) check to make sure the gestureRecognizer is the tapGestureRecognizer
if (touch.view == tableView)
{
return NO;
}
else
{
return YES;
}
}
To better fit what you need, judge if your search bar is first responder.
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gesture shouldReceiveTouch:(UITouch *)touch
{
BOOL isInsideTableView = CGRectContainsPoint(yourTabelView.frame, [touch locationInView:gesture.view]);
if (isInsideTableView && ![yourSearchBar isFirstResponder])
return NO;
return YES;
}

iOS Generic Touch Handler

Is there any such thing as a generic handler for any sort of touch event? A method that get called when any sort of interaction occurs with information about the touch event passed as a parameter.
You are going to want to adhere to the UIGestureRecognizerDelegate protocol, namely the following two methods:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer;
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch;
Within these methods you can check the gesture recognizer object to see if it is the type of gesture you are looking for and specify whether or not the gesture should begin or receive the touch.
EDIT: To detect the end of a gesture you would have something like this:
// in viewDidLoad or in initialization of your view controller:
_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanGestureRecognizer:)];
[self addGestureRecognizer:_panGestureRecognizer];
[_panGestureRecognizer setDelegate:self];
- (void)handlePanGestureRecognizer:(UIPanGestureRecognizer *)gesture {
UIGestureRecognizerState state = [gesture state];
if (state == UIGestureRecognizerStateEnded) {
// gesture ended here
}
}

UITapGestureRecognizer not working with UIButton

UITapGestureRecognizer *PressRecognizer1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handlePress:)];
[PressRecognizer1 setNumberOfTouchesRequired:1];
[firstBtn addGestureRecognizer:PressRecognizer1];
-(void)handlePress:(UITapGestureRecognizer*)PressRecognizer {
NSLog(#"working");
if (PressRecognizer.state == UIGestureRecognizerStateBegan) {
UIButton *whichButton=(UIButton *)[PressRecognizer view];
NSLog(#"whichButton %d\n",whichButton.tag);
if (whichButton.tag == 0) {
NSLog(#"currentImageId1 %d",currentImageId1);
[delegate imageZoom:currentImageId1];
}
I have created a UITabelView with customcell which has three images per row.When tap on a image hadlePress method called.But i does not come inside first if condition.
from apple docs:
Although taps are discrete gestures, they are discrete for each state of the gesture recognizer; thus the associated action message is sent when the gesture begins and is sent for each intermediate state until (and including) the ending state of the gesture. Code that handles tap gestures should therefore test for the state of the gesture, for example:
- (void)handleTap:(UITapGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateEnded)
{
// handling code
}
}
if you NSLog(#"%d",sender.state) inside the method (before the if statement) you'll see that the method is getting fired only with the UIGestureRecognizerStateEnded state, thus you should change your
if (PressRecognizer.state == UIGestureRecognizerStateBegan)
to
if (PressRecognizer.state == UIGestureRecognizerStateEnded)

Resources