You know how Mario just keeps running to the right when you press and hold the right-button on the D-Pad? In the same manner, I want my UIButton to continuously fire its action for the duration that it is held down. Is this possible for a UIButton? If not, is this possible to do with a UIImageView by overriding a touch handling method in a certain way? Actually, before trying to do get this done with UIButton I had some UIImageViews (Arranged to function as a D-Pad) that were checked by touch handling methods but things started to get messy so I thought this could be done easier with UIButton and thus switched over. Anybody who knows how to get recognition of a continuous, stationary (not-moved) down-touch, please share.
4 Answers
You can also do similar to what is shown in the previous answer and still use a UIButton.
Just have the timer started on the "Touch Down" and have the timer stopped on either "Touch Up Inside" or "Touch Up Outside".
Personally, I like using UIButtons because they offer some built in visual enhancements you don't have to code on your own.

- 1,179
- 7
- 15
-
9Just to be clear, both "Touch Up Inside" and "Touch Up Outside" events should be configured to stop the timer. – gerry3 Nov 24 '10 at 09:39
-
1In my opinion, better answer than the selected one. Can be configured via interface builder. Also, working with IBActions usually leads to cleaner and easier to understand code than touchesBegan and frieds. – txulu Dec 23 '13 at 15:37
-
1Don't forget to handle UIControlEventTouchCancel if you go down this approach. (Although I definitely recommend a UIButton subclass that encapsulates NSTimer. View controllers tend to be too large, so anything you can do to move the code _out_ of the view controller is a good thing.) – Andrey Tarantsov Sep 07 '14 at 02:00
-
UIControlEventTouchCancel doesn't works for me in case user holds the button and drags his finger outside of the button area. Instead of using TouchCancel event it's better to use (in my opinion) TouchDragExit. To conclude we are using: – Tzegenos Nov 26 '15 at 08:22
Don't use a button, use multi-touch and NSTimer:
Make a view-local NSTimer object inside your interface, then use it to start/cancel the timer
-(void)movePlayer:(id)sender {
<Code to move player>
}
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
timer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(movePlayer:) userInfo:nil repeats:YES];
}
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
if (timer != nil)
[timer invalidate];
timer = nil;
}
-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
if (timer != nil) {
[timer invalidate];
timer = nil;
}
}
This way, you can repeat the event at a predefined interval, and not have to rely on a button, and get the repeat behaviour you're looking for. Note the touchesMoved trigger - if they move their finger, this cancels the timer, and the player stops moving.

- 21,420
- 9
- 66
- 74

- 7,128
- 2
- 26
- 22
-
-
Great, though could you correct the duplicate touchesBegan trigger to your intended touchesMoved so nobody gets confused. – RexOnRoids May 24 '09 at 05:38
For me the following works:
- Create a button.
- Create 2 methods (stop touching and start touching) either in view controller or to a subclass.
- Add 3 Control Events. Touch Up Inside and Touch Drag Exit that both of them go to stop touching method and Touch Down goes with start touching method.
- When start touching method invokes we should start an NSTimer with interval approximately 0.2 (it's up to you how fast you would like to be invoked), repeat true and as a selector a method that you want to be invoked (having the actual stuff you want to execute when user hits the button).
- When stop touching method invokes we should invalidate timer
(
.invalidate()
) and assign timer as nil.
That's all!

- 837
- 12
- 16
And Now for Something Completely Different:
ReactiveCocoa 6.
self.button.reactive
.controlEvents([.touchDown])
.observeValues { button in
SignalProducer.timer(interval: .milliseconds(500), on: QueueScheduler.main)
.take(until: button.reactive.controlEvents([.touchDragOutside, .touchDragExit, .touchUpInside, .touchUpOutside, .touchCancel]).map { _ in return })
.prefix(value: Date())
.startWithValues { date in
NSLog("\(date)")
}
}

- 1,897
- 16
- 24