I d'like to combine a UILongPressGestureRecognizer with a UIPanGestureRecognizer.
The UIPanGestureRecognizer should start with a long press. Is there a simple way to do this? or do I really have to write my own gesture recognizer?
I wan't something like on the home screen. You press on an icon and after some time the icons start wobbling. Afterwards without releasing my finger from the screen I can start dragging the icon under my finger around.
actually, you don't have to combine gesture recognizers - you can do this solely with UILongPressGestureRecognizer... You enter StateBegan once your touch(es) have stayed within 'allowableMovement' for 'minimumPressDuration'. You stay in your continuous longPressGesture as long as you don't lift any of your fingers - so you can start moving your fingers and track the movement through StateChanged.
Long-press gestures are continuous. The gesture begins (UIGestureRecognizerStateBegan) when the number of allowable fingers (numberOfTouchesRequired) have been pressed for the specified period (minimumPressDuration) and the touches do not move beyond the allowable range of movement (allowableMovement). The gesture recognizer transitions to the Change state whenever a finger moves, and it ends (UIGestureRecognizerStateEnded) when any of the fingers are lifted.
I found a solution:
This UIGestureRecognizerDelegate method does exactly what I looked for:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
I had a bit of a hard time for this problem. The accepted answer wasn't enough. No matter what I put in that method the pan or longpress handlers would get invoked. A solution I found was as follows:
- Ensure the gesture recognizers' delegates are assigned to the same class (in my case self) and ensure the delegate class is a
UIGestureRecognizerDelegate
.
Add the following delegate method to your class (as per the answer above):
- (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
Add the following delegate method to your class:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && ! shouldAllowPan) {
return NO;
}
return YES;
}
Then add either a property or ivar which will track if the pan should be allowed to begin (see method above). In my case BOOL shouldAllowPan
.
Set the BOOL to NO
in your init
or viewDidLoad
. Inside your longPress handler set the BOOL to YES
. I do it like this:
- (void) longPressHandler: (UILongPressGestureRecognizer *) gesture {
if(UIGestureRecognizerStateBegan == gesture.state) {
shouldAllowPan = NO;
}
if(UIGestureRecognizerStateChanged == gesture.state) {
shouldAllowPan = YES;
}
}
Inside the panHandler I do a check on the BOOL:
- (void)panHandler:(UIPanGestureRecognizer *)sender{
if(shouldAllowPan) {
// do your stuff
}
And finally reset the BOOL within the panHandler:
else if(sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateFailed || sender.state == UIGestureRecognizerStateCancelled) {
shouldAllowPan = NO;
}
And then go grab a beer to congratulate yourself. ;)
Andy B's approach in Swift,
Add the UIGestureRecognizerDelegate delegate to the class
class ViewController: UIViewController, UIGestureRecognizerDelegate
Add a member variable
var shouldAllowPan: Bool = false
Add the gestures and need to add the pan gesture delegate to the VC. This is needed to fire off the shouldRecognizeSimultaneouslyWithGestureRecognizer and gestureRecognizerShouldBegin functions
// long press
let longPressRec = UILongPressGestureRecognizer(target: self, action: "longPress:")
yourView.addGestureRecognizer(longPressRec)
// drag
let panRec = UIPanGestureRecognizer(target: self, action: "draggedView:")
panRec.delegate = self
yourView.addGestureRecognizer(panRec)
Allow simultaneous gestures
func gestureRecognizer(UIGestureRecognizer,
shouldRecognizeSimultaneouslyWithGestureRecognizer:UIGestureRecognizer) -> Bool {
// println("shouldRecognizeSimultaneouslyWithGestureRecognizer");
return true
}
func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
// We only allow the (drag) gesture to continue if it is within a long press
if((gestureRecognizer is UIPanGestureRecognizer) && (shouldAllowPan == false)) {
return false;
}
return true;
}
Inside the long press handler:
func longPress(sender: UILongPressGestureRecognizer) {
if(sender.state == .Began) {
// handle the long press
}
else if(sender.state == .Changed){
shouldAllowPan = true
}
else if (sender.state == .Ended) {
shouldAllowPan = false
}
}
Read the "Subclassing Notes" section of Apple's UIGestureRecognizer Class Reference at:
https://developer.apple.com/library/prerelease/tvos/documentation/UIKit/Reference/UIGestureRecognizer_Class/