iOS Combining longPress and swipe gesture - ios

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

Related

Why is the UITapGestureRecognizer never getting called with state began?

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.

How to detect long press within a view and its subviews swift

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?

Gesture Recogniser for all events

I have this piece of code
let window = UIApplication.sharedApplication().keyWindow
let tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
window?.addGestureRecognizer(tap)
It only recognizes Tap (as its tap gesture recognizer), however I want some recognizer to work for all, scroll, pinch, etc.
Also at later point of time I am removing this recognizer.
I read touches began, but how to use it on UIApplication.sharedApplication().keyWindow?
And how to remove that touches began later?
If you want to monitor all touch event then you can subclass UITapGestureRecognizer, this link should help: Monitoring all iOS touches

Long Press Gesture Recognizer Only Fired When Finger is Lifted

I'm having an interesting problem with a long press gesture recognizer. I placed one of these on a UITableView, and it only works when I lift my finger after the long press. So basically, I would place my finger on a cell, and then when I lift my finger, it triggers the long press. I figured this out by putting printns when the long press began and ended and both fire after I lift my finger. I think the tableViews default panGestureRecognizer might be interfering with the longPressGestureRecognizer. Here is my code in viewDidLoad:
var longPress:UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "handleLongPress:")
longPress.minimumPressDuration = 0.06
longPress.delegate = self
self.tableView.addGestureRecognizer(longPress)
longPress.requireGestureRecognizerToFail(self.tableView.panGestureRecognizer)
Touching down in the cell will not cause the table view's panGestureRecognizer to fail, so delete the requireGestureRecognizerToFail method, and you should then get to the .Began state while your finger is still down.

Give swipe gesture priority over button

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...

Resources