4

I'm trying to allow some UIButton instances on one of my views to be touched and dragged around the screen (eventually with momentum, but that's for later!). I have this working in a very simple form, shown below, but the problem is that by touching the button to begin dragging it, it attaches to the finger, and by lifting the finger off, the "Touch Up Inside" event is triggered, which is the code I want to execute when actually tapping the button.

In a nutshell: how do I differentiate between a tap, and a drag/release? Do I need to change the tap to a short-tap gesture recognizer, or similar, perhaps? Code:

In viewDidLoad:

[firstButton addTarget: self action: @selector(wasDragged: withEvent:) forControlEvents: UIControlEventTouchDragInside];

And my wasDragged method:

- (void)wasDragged:(UIButton *)button withEvent:(UIEvent *)event
{
    if (button == letter1Button) {
        UITouch *touch = [[event touchesForView:button] anyObject];

        CGPoint previousLocation = [touch previousLocationInView:button];
        CGPoint location = [touch locationInView:button];
        CGFloat delta_x = location.x - previousLocation.x;
        CGFloat delta_y = location.y - previousLocation.y;

        button.center = CGPointMake(button.center.x + delta_x, button.center.y + delta_y);
    }
}
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Luke
  • 9,512
  • 15
  • 82
  • 146

3 Answers3

16

You could use a UIPanGestureRecognizer and tell it to cancel touches in view...

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIPanGestureRecognizer *panRecognizer;
    panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self
                                                            action:@selector(wasDragged:)];
    // cancel touches so that touchUpInside touches are ignored
    panRecognizer.cancelsTouchesInView = YES;
    [[self draggableButton] addGestureRecognizer:panRecognizer];

}

- (void)wasDragged:(UIPanGestureRecognizer *)recognizer {
    UIButton *button = (UIButton *)recognizer.view;
    CGPoint translation = [recognizer translationInView:button];

    button.center = CGPointMake(button.center.x + translation.x, button.center.y + translation.y);
    [recognizer setTranslation:CGPointZero inView:button];
}

- (IBAction)buttonWasTapped:(id)sender {
    NSLog(@"%s - button tapped",__FUNCTION__);
}
FluffulousChimp
  • 9,157
  • 3
  • 35
  • 42
  • thanks for this great tip. ***Note however that*** bizarrely, I had a situation where: doing it via storyboard: you actually have to set cancelsTouchesInView ***off*** on the storyboard: i've no idea why. – Fattie May 23 '16 at 14:26
2

For beginners like me, I tried UIPanGestureRecognizer as suggested above, but it did not work. So, here is my simple solution: First, add event listeners as suggested by Baig:

// add drag listener
[button addTarget:self action:@selector(wasDragged:withEvent:) forControlEvents:UIControlEventTouchDragInside];
// add tap listener
[button addTarget:self action:@selector(wasTapped:) forControlEvents:UIControlEventTouchUpInside];

Both drag and tap will both trigger UIControlEventTouchUpInside, so add a flag in wasDragged:withEvent: like this:

-(IBAction)wasDragged: (id)sender withEvent: (UIEvent *) event {
    was_dragged = YES;
    UIButton *selected = (UIButton *)sender;
    selected.center = [[[event allTouches] anyObject] locationInView:self.view];
}

- (IBAction)buttonWasTapped:(id)sender {
    if(!was_dragged)
        NSLog(@"button tapped");
    else{
        was_dragged = NO;
        NSLog(@"button dragged");
    }
}

Voila. Done.

HSH
  • 111
  • 1
  • 9
1

Use UIControlEventTouchDragInside and UIControlEventTouchUpInside events of UIButton like

// add drag listener
[button addTarget:self action:@selector(wasDragged:withEvent:) forControlEvents:UIControlEventTouchDragInside];
// add tap listener
[button addTarget:self action:@selector(wasTapped:) forControlEvents:UIControlEventTouchUpInside];

You can use HBDraggableButton control..

Baig
  • 4,737
  • 1
  • 32
  • 47
  • I had an issue where my UIButton was getting set to highlighted when a user dragged off of them (not triggering the action). I discovered this case fell under UIControlEventTouchCancel. Your answer led me to that; thanks. – vikzilla Feb 14 '16 at 00:22