12

I want to have a certain method run and repeat itself for as long as someones finger is pressed down on a button. I want that method to stop repeating itself when the finger is not on the button anymore

Is there a way to check if the touchDown is still occurring during the method implementation? Help!

rmaddy
  • 314,917
  • 42
  • 532
  • 579
dokun1
  • 2,120
  • 19
  • 37
  • 2
    possible duplicate of [Way to make a UIButton continuously fire during a press-and-hold situation?](http://stackoverflow.com/questions/903114/way-to-make-a-uibutton-continuously-fire-during-a-press-and-hold-situation) – rmaddy May 17 '13 at 18:21
  • this way worked! thank you. I didn't quite understand it when I read it through before posting this question - second time's a charm. – dokun1 May 17 '13 at 18:38

2 Answers2

15

You can use the UIControlEventTouchDown control event to start the method running, and UIControlEventTouchUpInside, or similar, to detect when the button is no longer being "pressed".

Set up the actions to the button, e.g.:

[myButton addTarget:self action:@selector(startButtonTouch:) forControlEvents:UIControlEventTouchDown];
[myButton addTarget:self action:@selector(endButtonTouch:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];

(Note the above will cause touch up inside and outside the button to invoke the endButtonTouch: method.)

Then add the startButtonTouch: and endButtonTouch methods, e.g., :

- (void)startButtonTouch:(id)sender {
    // start the process running...
}

- (void)endButtonTouch:(id)sender {
// stop the running process...
}
bobnoble
  • 5,794
  • 3
  • 25
  • 32
  • This approach will not work if the user taps on the button and move the finger outside the button's bounds. – danypata May 17 '13 at 18:27
  • 1
    Use both `UIControlEventTouchUpInside` and `UIControlEventTouchUpOutside`. Added to the answer. If you want to end it when the user drags outside the button, add `UIControlEventTouchDragExit` as well. – bobnoble May 17 '13 at 18:37
0

Drawing upon the bobnoble's answer here's a helper view

#import <UIKit/UIKit.h>

@interface YOIDCAutorepeatingButton : UIButton

// you COULD pinch pennies switching to nonatomic, but consider
// how much time it would take to debug if some day some moron decides without checking this spec
// to alter this prop off another thread
@property (atomic) NSTimeInterval delayUntilAutorepeatBegins;
@property NSTimeInterval delayBetweenPresses;

@property (weak) id<NSObject> recipient;
@property SEL touchActionOnRecipient;

@end

-------------> .m

#import "YOIDCAutorepeatingButton.h"

@interface YOIDCAutorepeatingButton()
{
NSTimeInterval _pressStartedAt;
}

@end

@implementation YOIDCAutorepeatingButton


- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
    [self addTarget:self action:@selector(startButtonTouch:) forControlEvents:UIControlEventTouchDown];
    [self addTarget:self action:@selector(endButtonTouch:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];

    self.delayUntilAutorepeatBegins = .250;
    self.delayBetweenPresses = .080;
}
return self;
}

-(void)killLastCharacter:(id)sender
{
[self.recipient performSelector:self.touchActionOnRecipient withObject:sender];
}

- (void)performAutorepeat:(id)sender
{
if(!self.delayBetweenPresses) {
    // bail
    return;
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.delayBetweenPresses * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    if(_pressStartedAt) {
        [self killLastCharacter:sender];
        [self performAutorepeat:sender];
    }
});
}

- (void)startButtonTouch:(id)sender {

[self killLastCharacter:sender];
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
_pressStartedAt = now;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.delayUntilAutorepeatBegins * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    if(_pressStartedAt) {
        [self killLastCharacter:sender];
        [self performAutorepeat:sender];
    }
});
}

- (void)endButtonTouch:(id)sender {
_pressStartedAt = 0;
}

@end

-------- sample usage -------

- (IBAction)killLastDigit:(id)sender {

.....

- (void)viewDidLoad
{
assert(self.backSpace);
[YOIDCAutorepeatingButton class]; // if xib is in a bundle other than main gottal load the class
// otherwise you'd get -[UIButton setRecipient:]: unrecognized selector sent to instance
// on setRecipient:
self.backSpace.recipient = self;
self.backSpace.touchActionOnRecipient = @selector(killLastDigit:);
Anton Tropashko
  • 5,486
  • 5
  • 41
  • 66