Reputation: 6147
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.
Upvotes: 20
Views: 12038
Reputation: 43
I solved this issue by implementing the desired functionality of the "action: Selector?" func of the UIPanGestureRecognizer within the "action: Selector?" func for the UILongPressGestureRecognizer.
As 'UILongPressGestureRecognizer' has no member 'translation', I calculated the translation by saving the position of the original touch and them extracting it from the actual touch position.
// in target class
var initialTouchX : CGFloat
var initialTouchX : CGFloat
// in the @objc func for the UILongPressGestureRecognizer
if sender.state == .began {
initialTouchX = sender.location(in: sender.view).x
initialTouchY = sender.location(in: sender.view).y
}
let translation = CGVector(dx: sender.location(in: sender.view).x - initialTouchX, dy: sender.location(in: sender.view).y - initialTouchY)
Upvotes: 0
Reputation: 83
For combinate more gesture :
var shouldAllowSecondGesture : Bool = false
let longPressRec = UILongPressGestureRecognizer(target: self, action: #selector(self.startDrag(sender:)))
cell.addGestureRecognizer(longPressRec)
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(sender:)))
cell.isUserInteractionEnabled = true
cell.addGestureRecognizer(panGestureRecognizer)
Extension your VC and implement GestureRecognizerDelegate for implemented this method.
extension YourViewController : UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
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
}
@objc func startDrag(sender:UIPanGestureRecognizer) {
if(sender.state == .began) {
// handle the long press
}
else if(sender.state == .changed){
shouldAllowPan = true
}
else if (sender.state == .ended) {
shouldAllowPan = false
}
}
Upvotes: 1
Reputation: 1527
Read the "Subclassing Notes" section of Apple's UIGestureRecognizer Class Reference at:
Upvotes: 0
Reputation: 10301
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
}
}
Upvotes: 11
Reputation: 629
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:
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. ;)
Upvotes: 21
Reputation: 296
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.
Upvotes: 24
Reputation: 6147
I found a solution: This UIGestureRecognizerDelegate method does exactly what I looked for:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
Upvotes: 16