将长按手势和拖动手势结合在一起

43

我正在移动我的视图

UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(moveRight:)];
[panRecognizer setMinimumNumberOfTouches:1];
[panRecognizer setMaximumNumberOfTouches:1];
[panRecognizer setDelegate:self];
[bubbleView[rightCnt] addGestureRecognizer:panRecognizer];
[panRecognizer release];

现在,我想通过长按拖动来完成同样的操作。

有什么想法吗?

5个回答

91
UILongPressGestureRecognizer已经为您做了您想要的事情。请查看UIGestureRecognizerState属性。根据文档

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

因此,在调用UILongPressGestureRecognizer选择器后,您需要监听、和。在期间持续更改您的视图帧。

- (void)moveRight:(UILongPressGestureRecognizer *)gesture
{
    if(gesture.state == UIGestureRecognizerStateBegan)
    {
        //if needed do some initial setup or init of views here
    }
    else if(gesture.state == UIGestureRecognizerStateChanged)
    {
        //move your views here.
        [yourView setFrame:];
    }
    else if(gesture.state == UIGestureRecognizerStateEnded)
    {
        //else do cleanup
    }
}

3
我正在使用CGPoint translatedPoint = [(UILongPressGestureRecognizer*)sender translationInView:self.view]; 但是对于长按,我需要使用什么? - PJR
尝试使用[yourView setFrame:CGRectMake(xCoord,yCoord,height,width)] - Srikar Appalaraju
4
但是通过这种方式,我怎么能获得翻译后的点呢? - PJR
为什么你需要 translatedPoint?你想要长按视图并将其拖动,对吗?在 UIGestureRecognizerStateEnded 中,你编写代码来获取该视图的新位置。 - Srikar Appalaraju
5
@SrikarAppal你没有完全回答问题。PJR想知道如何在每次UIGestureRecognizerStateChanged被调用时获取手指坐标。答案是:使用[myGestureRecognizer locationInView:aView] - Martin
非常感谢,我想指出这是检查长按手势和拖动到特定UIView的最佳解决方案。省去了研究touchesmoved等的麻烦。谢谢! - Munib

37
@implementation MyViewController {
    CGPoint _priorPoint;
}

- (void)moveRight:(UILongPressGestureRecognizer *)sender {
    UIView *view = sender.view;
    CGPoint point = [sender locationInView:view.superview];
    if (sender.state == UIGestureRecognizerStateChanged) {
        CGPoint center = view.center;
        center.x += point.x - _priorPoint.x;
        center.y += point.y - _priorPoint.y;
        view.center = center;
    }
    _priorPoint = point;
}

最初你会把priorPoint设置为什么? - RanLearns
你不需要将它设置为特定的值,因为 sender.state 在变为 Changed 之前总是会先变为 Began。这意味着 _priorPoint 在使用之前总是会被初始化。 - rob mayoff
但是,priorPoint 最初的值为 (0.0, 0.0),所以使用这段代码后,初始触摸会将对象的中心移动超过点位置与中心位置之间的差值。我不得不在“Began”状态下将 priorPoint 设置为视图的中心。 - RanLearns
对于未来的读者 - 我希望将UIPanGestureRecognizer的拖动动作与使用UITapGestureRecognizer在初始轻击时执行操作的能力相结合。我尝试了这里针对UILongPressGestureRecognizer的答案,但是虽然这似乎对其他人有效,但随着对象移动的位置计算并没有达到使用UIPanGestureRecognizer的质量 - 这就是我最终返回使用的原因。对我来说,这个答案有助于提供识别同时手势识别器的解决方案。 - RanLearns

5
在Swift中,可以使用以下代码来实现此操作。
class DragView: UIView { 
  // Starting center position
  var initialCenter: CGPoint?

  override func didMoveToWindow() {
    super.didMoveToWindow()
    // Add longPress gesture recognizer
    let longPress = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction(gesture:)))
    addGestureRecognizer(longPress)
  }

  // Handle longPress action
  func longPressAction(gesture: UILongPressGestureRecognizer) {
    if gesture.state == .began {
        guard let view = gesture.view else {
            return
        }
        initialCenter = gesture.location(in: view.superview)
    }
    else if gesture.state == .changed {
        guard let originalCenter = initialCenter else {
            return
        }

        guard let view = gesture.view else {
            return
        }

        let point = gesture.location(in: view.superview)

        // Calculate new center position
        var newCenter = view.center;
        newCenter.x += point.x - originalCenter.x;
        newCenter.y += point.y - originalCenter.y;

        // Update view center
        view.center = newCenter
    }
    else if gesture.state == .ended {
       ...
    }
}

2

感谢Hari Kunwar提供的Swift代码,但是longPressAction函数没有正确定义。

这里是改进后的版本:

@objc func longPressAction(gesture: UILongPressGestureRecognizer)  {
    if gesture.state == UIGestureRecognizerState.began {
    }
    else if gesture.state == .changed {
        guard let view = gesture.view else {
            return
        }
        let location = gesture.location(in: self.view)
        view.center = CGPoint(x:view.center.x + (location.x - view.center.x),
                                          y:view.center.y + (location.y - view.center.y))
    }
    else if gesture.state == UIGestureRecognizerState.ended{
    }
}

2

您不需要声明_priorPoint;

在我的情况下,我只想让视图水平移动,因此我只改变了x坐标。

以下是我的解决方案:

    if (longpressGestRec.state == UIGestureRecognizerStateChanged)
    {
        UIView *view = longpressGestRec.view;

        // Location of the touch within the view.
        CGPoint point = [longpressGestRec locationInView:view];

        // Calculate new X position based on the amount the gesture
        // has moved plus the size of the view we want to move.
        CGFloat newXLoc = (item.frame.origin.x + point.x) - (item.frame.size.width / 2);
        [item setFrame:CGRectMake(newXLoc,
                                  item.frame.origin.y,
                                  item.frame.size.width,
                                  item.frame.size.height)];
    }

3
你的解决方法只适用于跟随手指移动的UIView。想象一下,如果你从图片的角落开始执行UILongPress,然后你想向左滑动,那么图片会跳到手指的位置。在我的情况下,我不得不使用_priorPoint解决方案。 - Fabio

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