I have two UIButtons in a view (one is YES, the other NO). I now want to add a "did not answer", which would be indicated by a downward swipe on the view.
The problem is that the user may swipe down on the view, but in the process hit one of the buttons. When this happens I want to ignore the button press if there was a swipe underway. If it is just a tap on a button, the answer is recorded.
So, if the swipe occurs, I want to call the swipe gesture's action method. If it is determined no swipe occurred but one of the two buttons was touched I want to call their respective action methods. But if a button was touched in the process of a swipe, I want to call only the swipe gesture's action method.
I know there is a way but I'm wondering whether there is an EASY way of doing this. TIA for suggestions.
Since UISwipeGestureRecognizer is a "discrete" gesture, it just triggers a single action when recognized and it won't allow you to detect the end of the gesture.
So to prevent other touches during the gesture, I'd recommend using a UIPanGestureRecognizer instead since it can track your gesture from beginning to end. Then you can try setting your gesture's cancelsTouchesInView property to YES to cancel all other touches in the view that happen while that pan gesture is recognized, ex:
gesture.cancelsTouchesInView = YES;
gesture.delaysTouchesBegan = YES;
Your buttons should be registering touch up in view, so that someone who taps on the button can drag off if they decide not to proceed.
For your other swipe gesture, your buttons will not register a touch up in view during a swipe gesture, even if the swipe passes over the button or ends on it.
Okay, this seems to work. On the gesture recognizer, I used:
UISwipeGestureRecognizer *swipeDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(didSwipeDown:)];
swipeDown.direction = UISwipeGestureRecognizerDirectionDown;
swipeDown.delaysTouchesBegan = YES;
swipeDown.delegate = self;
[self addGestureRecognizer:swipeDown];
They delaysTouchesBegan = YES allows it to determine whether the swipe has occurred before passing the touches on to the buttons. So, if you swipe, it calls the swipe GR, and if you touch either button, you get that. Thanks for your answers...
Related
If you assign a UITapGestureRecognizer to a UIView the UIGestureRecognizerStateBegan doesn't appear when the user has touched the view.
// Tap
_tapGestureRecognizer =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(tap:)];
[_someView addGestureRecognizer:_tapGestureRecognizer];
Instead the recognizer jumps straight to UIGestureRecognizerStateEnded when the user performs the tap.
I have to change that view to a UIButton and listen to the touchDown method.
_someButton = [[UIButton alloc] init];
[_someButton addTarget:self action:#selector(touchDown:) forControlEvents:UIControlEventTouchDown];
[self addSubview: _someButton];
I don't like changing the UIView to a UIButton just for this.
Can I use the UITapGestureRecognizer instead?
Let me start by saying that UITapGestureRecognizer docs clearly tell to expect a callback for all states.
For gesture recognition, the specified number of fingers must tap the view a specified number of times. Although taps are discrete gestures, they are discrete for each state of the gesture recognizer. The system sends the associated action message when the gesture begins and then again for each intermediate state until (and including) the ending state of the gesture. Code that handles tap gestures should test for the state of the gesture, for example:
func handleTap(sender: UITapGestureRecognizer) {
if sender.state == .ended {
// handling code
}
}
Hower it makes little to no sense (specially in case of single tap recognizer). You touched a view (that had the tap gesture added to it), you haven't yet lifted your finger, moved it etc. System can't know at the time of .touchDown event that this interaction is going to turn into a successful recognition of a tap (which requires lifting the finger up).
Essentially UITapGestureRecognizer (for a single touch tap) is a .touchDown + .touchUp combination. If anything else happens after .touchDown like a drag (.touchDragInside OR .touchDragExit), it may lead to successful recognition of a pan gesture (tableView scrolling etc.)
You can think of UITapGestureRecognizer roughly equivalent to .touchUpInside event for a button. A .touchUpInside event for a button doesn't call your function for .touchDown event, It is only possible to receive that event by explicitly asking for the same.
Why do the docs say so?
Maybe system is able to identify the .began state for other scenarios
a multi-tap gesture - double/triple tap (see UITapGestureReconizer.numberOfTapsRequired)
a multi-touch tap - 2/3 finger tap (see UITapGestureReconizer.numberOfTouchesRequired)
You have to test other scenarios for this if you want to know more.
I have a view (I'll call it parentView) that has about 20-30 subviews. I have added a long press gesture recognizer to the parentView. The gesture recognizer only seems to fire when I press on the parentView, it does not fire when I press and hold on one of the subviews.
I have tried adding the gesture recognizer to self.view and using the gesture location to see if it was within the bounds of the parentView. However the same problem occurs since it does not seem to detect my long press on the subviews.I have also tried running a for loop and adding the gesture recognizer to each individual subview but this also did not work.
This is how I am defining my gesture recognizer if anybody was wondering.
longPress.minimumPressDuration = 1
longPress.addTarget(self, action: #selector(ViewController.handleLongPress)
parentView.addGestureRecognizer(longPress)
How would I get the long press gesture recognizer to detect a long press on the parentView and it's subviews?
I am currently facing a scenario that I am using a UIPageViewController on a view & I want the right swipe gesture to work when user swipe right side from index value 0, so I am trying to add a right swipe gesture to UIPageViewController by the following code:
UISwipeGestureRecognizer *rightRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(rightSwipeHandle:)];
rightRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
[rightRecognizer setNumberOfTouchesRequired:1];
//Edit 1
[self.pageViewController.view addGestureRecognizer:rightRecognizer];
but, while I am adding gesture to pageviewcontroller I am getting following error message (Solved by #Lyndsey Scott answer)
no visible interface uipageviewcontroller declares the selector addgesturerecognizer
Can anyone guide me that how can I implement the right swipe in this pageViewController
Edit 1: Replaced the code provided by #Lyndsey Scott, removed the error, but problem still exists that I am not able to trigger that swipe event.
You have to the gesture to the UIPageViewController's view:
[self.pageViewController.view addGestureRecognizer:rightRecognizer];
Edit in response to your edit:
The swipe gesture won't work if you haven't implemented any UIGestureRecognizerDelegate methods to allow for the UIPageViewController's swipe gesture and your swipe gesture to be recognized simultaneously. Right now, your swipe is essentially being blocked by the page controller's gesture. You could change that by implementing the UIGestureRecognizerDelegate, setting rightRecognizer.delegate = self;, and overriding gestureRecognizer: shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer such that it returns true in the case that both gesture recognizers are triggered.
But if you want the page to flip and your gesture to be recognized at the time of that page flip, adding the extra swipe gesture is unnecessary since you can simply trigger your method upon page turn; for example in pageViewController:didFinishAnimating:previousViewControllers: transitionCompleted:.
You shouldn't have to add the gesture recognizer, if you implement the protocol correctly it should work out of the box.
The point of UIPageViewController is to not have to do it manually.
I add a swipeUp gesture to the whole view.
I add a longPressGestureRecognizer to the whole view, set its minimunPressDuration equals 0.001f so that it can both detect press down action and touches move action, then call the requireGestureToFail function:
UILongPressGestureRecognizer *longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressed:)];
longPressGestureRecognizer.minimumPressDuration = 0.001f;
[longPressGestureRecognizer requireGestureRecognizerToFail:swipeGestureRecognizer];
The problem is:
When user press and hold (don't move) a button, the longPress gesture's state remains UIGestureStatePossible because the swipeUp gesture doesn't fail, So that it won't react to user touch.
If I don't call requireGestureRecognizerToFail all the gesture including swipeUp gesture will be recognised as longPress gesture.
Implmenting shouldRecognizeSimultaneouslyWithGestureRecognizer: is not what I expect.
What I want is when press and hold(don't move) a button, it triggers longPress, then if user swipe up it triggers swipeUp gesture, if user drags but the touch pattern doesn't fit swipeUp it still triggers longPress.
How can I implement this?
I think it would be easier to implement your own UIGestureRecognizer or UIView subclass with multiple gestures. Check this out:
UITapGestureRecognizer - make it work on touch down, not touch up?
https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/GestureRecognizer_basics/GestureRecognizer_basics.html#//apple_ref/doc/uid/TP40009541-CH2-SW2
I want the user to be able to swipe left, right, upward and downward on a UIWebview, but I don't want the HTML/Javascript to process those swipes-- I want to do that myself.
Is that possible: To intercept just the swipes? I want the taps still to go through to the UIWebview.
Thanks.
Sure thing.Just attach UIGestureRecognizer subclass to that view and hold on for calls...
UISwipeGestureRecognizer* leftSwipeRecognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(someAction)];
leftSwipeRecognizer.numberOfTouchesRequired = 1;
leftSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
leftSwipeRecognizer.cancelsTouchesInView = YES;
[self.webView addGestureRecognizer:leftSwipeRecognizer];
there is some sample for left swipe gesture. You can set more with very similar approach...Hope that helps.
If you add a UISwipeGestureRecognizer onto the UIWebview with
addGestureRecognizer:
it will allow you to get those events. I believe the default is to only allow one recognizer at a time, but I'm not sure if that applies to individual gesture types or across all gesture types, so this may only be the first step in solving your problem.