How to use UIPanGestureRecognizer to move object? iPhone/iPad

Viewed 125283

There are several examples of the UIPanGestureRecognizer class. For example I have read this and I am still not able to use it...

On the nib file that I am working on I have a UIView (white rectangle on image) that I wish to drag with that class:

enter image description here

and in my .m file I have placed:

- (void)setTranslation:(CGPoint)translation inView:(UIView *)view
{
    NSLog(@"Test to see if this method gets executed");
}

and that method does not get executed when I drag the mouse across the UIView. I have also tried placing:

- (void)pan:(UIPanGestureRecognizer *)gesture
{
    NSLog(@"testing");
}

And that method does not get executed either. Maybe I am wrong but I think this methods should work like the - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event method where I just have to place that method and it will get called whenever there are touches.

What am I doing wrong? Maybe do I have to draw a connection to that method? If so how can I do that?

9 Answers

@Tono Nam's answer (accepted) in Swift 5

func someMethod() {
    let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(move))
    panGestureRecognizer.minimumNumberOfTouches = 1
    panGestureRecognizer.maximumNumberOfTouches = 1
    view.addGestureRecognizer(panGestureRecognizer)
}

@objc func move(sender: UIPanGestureRecognizer) {
    guard let senderView = sender.view else { return }
    
    self.view.bringSubviewToFront(senderView)
    var translatedPoint: CGPoint = sender.translation(in: senderView.superview)
    
    if(sender.state == .began) {
        let firstX = senderView.center.x
        let firstY = senderView.center.y
    }
    
    translatedPoint = CGPoint(x: senderView.center.x + translatedPoint.x, y: senderView.center.y + translatedPoint.y)
    senderView.center = translatedPoint
    sender.setTranslation(.zero, in: senderView)
    
    if(sender.state == .ended) {
        let velocityX: CGFloat = (0.2 * sender.velocity(in: self.view).x)
        let velocityY: CGFloat = (0.2 * sender.velocity(in: self.view).y)
        
        var finalX: CGFloat = translatedPoint.x + velocityX
        var finalY: CGFloat = translatedPoint.y + velocityY
        
        if(finalX < 0) {
            finalX = 0
        } else if (finalX > self.view.frame.size.width) {
            finalX = self.view.frame.size.width;
        }
        
        if (finalY < 50) { // to avoid status bar
            finalY = 50;
        } else if (finalY > self.view.frame.size.height) {
            finalY = self.view.frame.size.height;
        }
        
        let animationDuration: TimeInterval = TimeInterval(abs(velocityX) * 0.0002 + 0.2)
        print("the animation duration is \(animationDuration)")
        
        UIView.animate(withDuration: animationDuration, delay: 0, options: .curveEaseOut) {
            senderView.center = CGPoint(x: finalX, y: finalY)
        } completion: { completed in
            // call animationDidFinish completion handler here
        }
    }
}

Casting my hat into the ring a couple years later.

Will need to save the beginning center of the image view:

var panBegin: CGPoint.zero

Then update the new center using a transform:

if recognizer.state == .began {
     panBegin = imageView!.center

} else if recognizer.state == .ended {
    panBegin = CGPoint.zero

} else if recognizer.state == .changed {
    let translation = recognizer.translation(in: view)
    let panOffsetTransform = CGAffineTransform( translationX: translation.x, y: translation.y)

    imageView!.center = panBegin.applying(panOffsetTransform)
}

As an addition, because I spend a lot of time on this: my view (which I applied addGestureRecognizer on) was dynamically created, so I did have to fix userInteractionEnabled to true

Related