¿Cómo usar UIPanGestureRecognizer para mover objetos? iPhone / iPad

80

Hay varios ejemplos de la UIPanGestureRecognizerclase. Por ejemplo, he leído esto y todavía no puedo usarlo ...

En el archivo nib en el que estoy trabajando, tengo un UIView(rectángulo blanco en la imagen) que deseo arrastrar con esa clase:

ingrese la descripción de la imagen aquí

y en mi archivo .m he colocado:

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

y ese método no se ejecuta cuando arrastro el mouse sobre el archivo UIView. También he intentado colocar:

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

Y ese método tampoco se ejecuta. Tal vez me equivoque, pero creo que estos métodos deberían funcionar como el - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)eventmétodo en el que solo tengo que colocar ese método y se llamará cada vez que haya toques.

¿Qué estoy haciendo mal? ¿Quizás tengo que establecer una conexión con ese método? Si es así, ¿cómo puedo hacer eso?

Tono Nam
fuente

Respuestas:

145

Encontré el tutorial Trabajando con UIGestureRecognizers , y creo que eso es lo que estoy buscando. Me ayudó a encontrar la siguiente solución:

-(IBAction) someMethod {
    UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(move:)];
    [panRecognizer setMinimumNumberOfTouches:1];
    [panRecognizer setMaximumNumberOfTouches:1];
    [ViewMain addGestureRecognizer:panRecognizer];
    [panRecognizer release];
}

-(void)move:(UIPanGestureRecognizer*)sender {
    [self.view bringSubviewToFront:sender.view];
    CGPoint translatedPoint = [sender translationInView:sender.view.superview];

    if (sender.state == UIGestureRecognizerStateBegan) {
        firstX = sender.view.center.x;
        firstY = sender.view.center.y;
    }


    translatedPoint = CGPointMake(sender.view.center.x+translatedPoint.x, sender.view.center.y+translatedPoint.y);

    [sender.view setCenter:translatedPoint];
    [sender setTranslation:CGPointZero inView:sender.view];

    if (sender.state == UIGestureRecognizerStateEnded) {
        CGFloat velocityX = (0.2*[sender velocityInView:self.view].x);
        CGFloat velocityY = (0.2*[sender velocityInView:self.view].y);

        CGFloat finalX = translatedPoint.x + velocityX;
        CGFloat finalY = translatedPoint.y + velocityY;// translatedPoint.y + (.35*[(UIPanGestureRecognizer*)sender velocityInView:self.view].y);

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

        CGFloat animationDuration = (ABS(velocityX)*.0002)+.2;

        NSLog(@"the duration is: %f", animationDuration);

        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:animationDuration];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
        [UIView setAnimationDelegate:self];
        [UIView setAnimationDidStopSelector:@selector(animationDidFinish)];
        [[sender view] setCenter:CGPointMake(finalX, finalY)];
        [UIView commitAnimations];
    }
}
Tono Nam
fuente
1
¡¡Asombroso!! Me encanta el cálculo de la animaciónDuración, funciona muy bien.
最 白 目
39
Si cambias move: (id) sender to be move: (UIPanGestureRecognizer *) sender no tienes que hacer todo ese encasillado feo.
Wex
1
Esta solución no funciona para UISwitch. También intenté implementar los métodos touchesBegan, touchesMoved pero no pude mover UISwitch. ¿Puede decirme cómo puedo mover UISwitch simplemente arrastrando?
DShah
2
Recuerde tener userInteractionEnabled = YES para la vista con la que está asociado el reconocedor de gestos.
Lukas Kalinski
1
¿Qué son firstX y firstY?
Krekin
39
UIPanGestureRecognizer * pan1 = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(moveObject:)];
pan1.minimumNumberOfTouches = 1;
[image1 addGestureRecognizer:pan1];

-(void)moveObject:(UIPanGestureRecognizer *)pan;
{
 image1.center = [pan locationInView:image1.superview];
}
Arun Kulasegaran
fuente
¡victoria impecable!
manuelBetancurt
1
No puedo discutir con la simplicidad, pero esta solución tiene un problema: tan pronto como pones el dedo hacia abajo para iniciar el gesto, image1saltará, potencialmente bastante lejos, de modo que su centro esté debajo de tu dedo. Es mejor ajustar image1.centerla pantraducción para una experiencia más fluida y estándar.
Sartak
25
-(IBAction)Method
{
      UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
      [panRecognizer setMinimumNumberOfTouches:1];
      [panRecognizer setMaximumNumberOfTouches:1];
      [ViewMain addGestureRecognizer:panRecognizer];
      [panRecognizer release];
}
- (Void)handlePan:(UIPanGestureRecognizer *)recognizer
{

     CGPoint translation = [recognizer translationInView:self.view];
     recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x, 
                                     recognizer.view.center.y + translation.y);
     [recognizer setTranslation:CGPointMake(0, 0) inView:self.view];

     if (recognizer.state == UIGestureRecognizerStateEnded) {

         CGPoint velocity = [recognizer velocityInView:self.view];
         CGFloat magnitude = sqrtf((velocity.x * velocity.x) + (velocity.y * velocity.y));
         CGFloat slideMult = magnitude / 200;
         NSLog(@"magnitude: %f, slideMult: %f", magnitude, slideMult);

         float slideFactor = 0.1 * slideMult; // Increase for more of a slide
         CGPoint finalPoint = CGPointMake(recognizer.view.center.x + (velocity.x * slideFactor), 
                                     recognizer.view.center.y + (velocity.y * slideFactor));
         finalPoint.x = MIN(MAX(finalPoint.x, 0), self.view.bounds.size.width);
         finalPoint.y = MIN(MAX(finalPoint.y, 0), self.view.bounds.size.height);

         [UIView animateWithDuration:slideFactor*2 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
             recognizer.view.center = finalPoint;
         } completion:nil];

    }

}
Mohit
fuente
3
Porque CGPointMake(0, 0)puedes escribir CGRectZero.
significado importa
3
@ significado-importa ¿No te refieres CGPointZero?
mylogon
2
@mylogon Tienes toda la razón. Subiré algunas de tus respuestas como agradecimiento.
significado importa
1
if ([recognizer state] == UIGestureRecognizerStateChanged)    
{
    CGPoint translation1 = [recognizer translationInView:main_view];
    img12.center=CGPointMake(img12.center.x+translation1.x, img12.center.y+ translation1.y);
    [recognizer setTranslation:CGPointMake(0, 0) inView:main_view];

    recognizer.view.center=CGPointMake(recognizer.view.center.x+translation1.x, recognizer.view.center.y+ translation1.y);
}

-(void)move:(UIPanGestureRecognizer*)recognizer
{
    if ([recognizer state] == UIGestureRecognizerStateChanged)    
    {
        CGPoint translation = [recognizer translationInView:self.view];
        recognizer.view.center=CGPointMake(recognizer.view.center.x+translation.x, recognizer.view.center.y+ translation.y);
        [recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
    }
}
PJRadadiya
fuente
1

Swift 2 versión de la respuesta de @MS AppTech

func handlePan(panGest : UIPanGestureRecognizer) {
let velocity : CGPoint = panGest.velocityInView(self.view);
        let magnitude : CGFloat = CGFloat(sqrtf((Float(velocity.x) * Float(velocity.x)) + (Float(velocity.y) * Float(velocity.y))));
        let slideMult :CGFloat = magnitude / 200;
        //NSLog(@"magnitude: %f, slideMult: %f", magnitude, slideMult);

        let slideFactor : CGFloat = 0.1 * slideMult; // Increase for more of a slide
        var finalPoint : CGPoint = CGPointMake(panGest.view!.center.x + (velocity.x * slideFactor),
                                         panGest.view!.center.y + (velocity.y * slideFactor));
        finalPoint.x = min(max(finalPoint.x, 0), self.view.bounds.size.width);
        finalPoint.y = min(max(finalPoint.y, 0), self.view.bounds.size.height);

        UIView.animateWithDuration(Double(slideFactor*2), delay: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
            panGest.view!.center = finalPoint;
            }, completion: nil)
}
hamidrezabstn
fuente
0

Lanzar mi sombrero al ring un par de años después.

Necesitará guardar el centro inicial de la vista de imagen:

var panBegin: CGPoint.zero

Luego actualice el nuevo centro usando una transformación:

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)
}
Un dulce
fuente
-2

La versión Swift 2:

// start detecting pan gesture
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(TTAltimeterDetailViewController.panGestureDetected(_:)))
panGestureRecognizer.minimumNumberOfTouches = 1
self.chartOverlayView.addGestureRecognizer(panGestureRecognizer)

func panGestureDetected(panGestureRecognizer: UIPanGestureRecognizer) {
    print("pan gesture recognized")
}
richdcs
fuente