Can we call different actions / delegates in response to the two different events of
Brad Larson's answer looks pretty good but here's another one that might give you a bit more flexibility/control of what you want or might want to do.
You subclass UIButton, you override the touchesBegan and touchesEnded methods so that when the user starts a touch you call
[self performSelector:@selector(detecetedLongTap) withObject:nil afterDelay:1.0];
and in the touchesEnded you call:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(detecetedLongTap) object:nil];
to cancel the event if the finger was lifted too soon.
You can get full code for this in this blog post:
http://www.isignmeout.com/adding-long-tap-functionality-uibutton/
Yes, it's reasonably easy to implement this using a UILongPressGestureRecognizer (on iPhone OS 3.2+). A long press will be handled by the gesture recognizer, and a short tap will pass through to the button's normal action.
For example, I subclassed UIButton and added the following method for specifying a long touch action to go along with a tap (longPressGestureRecognizer
is an instance variable):
- (void)setLongTouchAction:(SEL)newValue
{
if (newValue == NULL)
{
[self removeGestureRecognizer:longPressGestureRecognizer];
[longPressGestureRecognizer release];
longPressGestureRecognizer = nil;
}
else
{
[longPressGestureRecognizer release];
longPressGestureRecognizer = nil;
longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:[[self allTargets] anyObject] action:newValue];
[self addGestureRecognizer:longPressGestureRecognizer];
}
}
I then could do the following to set both short tap and long press actions that will be handled by the same target:
[undoButton addTarget:self action:@selector(performUndo:) forControlEvents:UIControlEventTouchUpInside];
[undoButton setLongTouchAction:@selector(showUndoOptions:)];
As you can see, this is useful for the undo buttons you see in title bars of many iPad applications.
The best solution I can think of, is to create another class, and subclass UIButton. Then on Interface Builder (if that's what you're using), you can set the button's class to the custom class you just created.
So in this new class, you have to override a method called
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
This is basically telling you that someone pressed down on your button. The touches is an NSSet and it holds all the information for all the fingers that are pressing down on the screen. If you only are interested in the one that's pressing on the button itself, you'll probably have something like:
NSSet *myTouches = [event touchesForView:self.view];
So now that you have the touches that correspond to your button, you have to find out how many times the user tapped on that button. You do that with something like:
NSUInteger numTaps = [[myTouches anyObject] tapCount];
If this number is 2, that means the user just double tapped your button. Now comes the next part. How do you know if the user is holding that button? Well when the user lets go of the screen, another method gets called. You'll need to override that one as well:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
This is where you know if the person has stopped touching the screen or if his finger is still on it. If his finger is still on it, then this event hasn't been called yet.
Now enough with the background
Here's my suggestion to you. I suggest you override the touchesBegan: method and check if the number of taps in the button is 2. If so, then start a timer that does what you need it to do, for as long as you need it to be done, and then on the touchesEnded: method, you'll go ahead and stop that timer, and this way you will have done whatever it is that you needed to do, for as long as you needed to do it OR as long as the user has held on to the button.
I hope this helps, obviously I didn't write the whole code for you, you'll have to experiment and research that stuff, but if you have any questions, I'll be happy to lend a helping hand :)