将UILongPressGestureRecognizer与UIPanGestureRecognizer结合使用

20

我想将UILongPressGestureRecognizer和UIPanGestureRecognizer结合起来。

UIPanGestureRecognizer应该从长按开始。有没有简单的方法可以做到这一点?还是我真的必须编写自己的手势识别器?

我想要类似于主屏幕的效果。您按住图标一段时间后,图标会开始摇晃。之后,在屏幕上不释放手指的情况下,我可以在我的手指下开始拖动图标。

7个回答

24

实际上,您不必结合手势识别器-您可以仅使用UILongPressGestureRecognizer来完成此操作... 当您的触摸停留在'allowableMovement'中达到'minimumPressDuration'时,您进入StateBegan状态。 只要不放开任何一个手指,您就会一直停留在长按手势中-因此,您可以开始移动手指并通过StateChanged跟踪移动。

长按手势是连续的。当允许的手指数量(numberOfTouchesRequired)已经按下指定时间(minimumPressDuration),且触摸没有超出允许的移动范围(allowableMovement)时,手势开始(UIGestureRecognizerStateBegan)。当任何一个手指移动时,手势识别器转换为Change状态,并在任何一个手指抬起时结束(UIGestureRecognizerStateEnded)。


6
虽然可能会遇到许多问题,但UIPanGestureRecognizer最好的一点在于它允许你拖动整个窗口并仍然返回值,而UILongPressGestureRecognizer则不会涉及子视图(至少是那些具有ClipsToBounds属性的子视图)。 - Kpmurphy91
4
当你进行长按操作时,无法获取到任何“速度(velocity)”信息。 - Scott Zhu

21

我在解决这个问题时遇到了一些困难。被采纳的答案并不足够。无论我在那个方法中放置什么,都会调用pan或longpress处理程序。我找到的解决方案如下:

  1. Ensure the gesture recognizers' delegates are assigned to the same class (in my case self) and ensure the delegate class is a UIGestureRecognizerDelegate.
  2. Add the following delegate method to your class (as per the answer above):

    - (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
    shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { 
         return YES;
    }
    
  3. Add the following delegate method to your class:

    - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
         if([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && ! shouldAllowPan) {
              return NO;
         }
         return YES;
    }
    
  4. 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.

  5. 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;
         }
    }
    
  6. Inside the panHandler I do a check on the BOOL:

    - (void)panHandler:(UIPanGestureRecognizer *)sender{
        if(shouldAllowPan) {
              // do your stuff
        }
    
  7. And finally reset the BOOL within the panHandler:

    else if(sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateFailed || sender.state == UIGestureRecognizerStateCancelled) {
        shouldAllowPan = NO;
    }
    
  8. And then go grab a beer to congratulate yourself. ;)


1
+1. 这些步骤真的帮助我解决了 OP 的目标:只允许长按后拖动。
只有两个评论:
在第 5 步中,只需检查 Began 然后设置为 YES 即可。 if(UIGestureRecognizerStateBegan == gesture.state) { self.shouldAllowDrag = YES; }而在第 6 步中,不必检查 shouldAllowPan。——对于未格式化的代码表示抱歉。
- Dirk van Oosterbosch
1
我实现了Andy的解决方案,它非常有效。为了获得我想要的响应能力,我还更改了最小长按持续时间:UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGesture:)]; lpgr.delegate = self; lpgr.minimumPressDuration = 0.05; - chmaynard
1
这对我帮助很大。谢谢!我会添加一个Swift解决方案,以便帮助其他人。 - duyn9uyen
非常好的回答。谢谢! - Matt Long

16
我找到了解决方案: 这个UIGestureRecognizerDelegate方法正好符合我所寻找的要求:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer

11

在Swift中,Andy B的方法如下:

  1. UIGestureRecognizerDelegate代理添加到该类中

class ViewController: UIViewController, UIGestureRecognizerDelegate
  • 添加一个成员变量

  • var shouldAllowPan: Bool = false
    
  • 添加手势并需要将 pan gesture delegate 添加到 VC 中。这是为了触发 shouldRecognizeSimultaneouslyWithGestureRecognizer 和 gestureRecognizerShouldBegin 函数。

  • // 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)
    
    允许同时手势。
    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;
    }
    
  • 在长按处理程序内:

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

  • 4
    你忘记了第六个:拿一瓶啤酒。 :) :) - Andy B
    1
    @duyn9uyen 感谢您将其转换为Swift。 - Matt Long
    @duyn9uyen 感谢您的转换,这个工作得非常好。 - aznelite89
    太棒了!谢谢!为了在我的项目中启用两个手势(而不需要使用shouldAllowPan),我只需要添加UIGestureRecognizerDelegate,将UIPanGestureRecognizer的代理设置为self,并添加gestureRecognizer(_: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith:UIGestureRecognizer)函数,并返回true。 - RanLearns
    @RanLearns 我也一样。 - Sentry.co

    1

    为了组合更多手势:

    1. 创建一个本地变量 var shouldAllowSecondGesture : Bool = false
    2. 创建两个识别器

    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)

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

    0

    我通过在“action: Selector?”函数中实现“UIPanGestureRecognizer”的所需功能来解决了这个问题,该函数是针对“UILongPressGestureRecognizer”的“action: Selector?”函数。

    由于“UILongPressGestureRecognizer”没有成员“translation”,因此我通过保存原始触摸的位置并从实际触摸位置中提取它来计算翻译。

    
    // 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)
    
    
    

    0

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接