23

I have several UIButtons which I use to set the current action when tapping in the main area. I would also like to allow the user to drag from the button directly into the main area and take the same action; essentially, the touchesBegan and touchesMoved should be passed on to the main view when touching the UIButtons, but also should send the button press action.

Right now, I have the touch up inside changing the control. The drag exit calls the touch up inside section to set the control, then calls the touches began section to start the main area touching operation.

However, at this point, the touchesMoved and touchesEnded are obviously not being called, because the touches originated on the UIButton.

Is there a way to half-ignore the touches so they're passed to the main area, but also allow me to set the control first?

Ed Marty
  • 39,590
  • 19
  • 103
  • 156

3 Answers3

36

I know this question is two years old (and already answered), but nevertheless...

When I tried this myself, the touches were forwarded, but the buttons no longer behaved like buttons. I also passed the touches along to "super" and now all is well.

So, for beginners that might stumble upon this, this is what the code should look like:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {  
    [super touchesBegan:touches withEvent:event];
    [self.nextResponder touchesBegan:touches withEvent:event]; 
}
daveMac
  • 3,041
  • 3
  • 34
  • 59
25

In the documentation, look for Responder Objects and the Responder Chain

You can "share" touches between objects by forwarding the touch up the responder chain. Your UIButton has a responder/controller that receives the UITouch events, my guess is that once it has preformed its interpretation of the message it returns - the touch has been handled and disposed of.

Apple suggests something like this (based on the type of touch of course):

[self.nextResponder touchesBegan:touches withEvent:event];

Rather than disposing of the touch event it is passed on.

Sub classed UIButton:

MyButton.h

#import <Foundation/Foundation.h>

@interface MyButton : UIButton {

}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event ;

@end

MyButton.m

#import "MyButton.h"

@implementation MyButton

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {  
    printf("MyButton touch Began\n");
    [self.nextResponder touchesBegan:touches withEvent:event]; 
}
@end
e.James
  • 116,942
  • 41
  • 177
  • 214
Paxic
  • 1,760
  • 16
  • 28
  • So in this case, I would have to subclass UIButton to pass the touches on, I presume? – Ed Marty Mar 11 '09 at 13:37
  • Subclassing should not be necessary, you will need to explore the chain a bit. The view or control that contains your button will be the next responder in the chain. – Paxic Mar 11 '09 at 16:42
  • 2
    I'm afraid I don't quite see how to trap messages from the UIButton and pass them on to the UIView without subclassing the UIButton to actually get the message. – Ed Marty Mar 11 '09 at 21:38
  • You are right, I was not thinking clearly. The easiest way to do this is by sub classing. You might want to look at editing the responder chain but sub classing will give you the result now. Sorry for goose chase. – Paxic Mar 11 '09 at 23:28
  • 3
    It's not necessary to declare the touchesBegan:: method in the header, as it is just being overridden, right? – jbrennan Jul 30 '09 at 16:08
  • 2
    You had a link above to the docs. I've been having this same problem and just read in those docs: "If you implement a custom view to handle events or action messages, you should not forward the event or message to nextResponder directly to send it up the responder chain. Instead invoke the superclass implementation of the current event-handling method—let UIKit handle the traversal of the responder chain." – Spanky Nov 14 '09 at 04:30
  • It's my first time doing. this and I am getting many errors like declaration types and more, sorry that I am answering. an old question but its not working I tried the other solutions too. Can they be a file with the complete code and the steps to reach there with Xcode, please – Camille Basbous Oct 05 '20 at 20:34
17

No need to subclass either! Simply stick this on the top of your implementation before anything else:

#pragma mark PassTouch

@interface UIButton (PassTouch)
@end

@implementation UIButton (PassTouch)
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];
    [self.nextResponder touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesMoved:touches withEvent:event];
    [self.nextResponder touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];
    [self.nextResponder touchesEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesCancelled:touches withEvent:event];
    [self.nextResponder touchesCancelled:touches withEvent:event];
}
@end
mattsven
  • 22,305
  • 11
  • 68
  • 104
Terry Han
  • 195
  • 1
  • 2
  • 3
    BTW there may still be a need to subclass. For instance, what if you needed to mix buttons that do need to pass along touches in the same view with buttons that for some reason you don't want to have that functionality? When I first tried this out I created a category on UIButton but found that because of the very reason I stated above, it was safer to use a subclass so I know exactly when I'm using a button that forwards the touches. – daveMac May 22 '12 at 22:39
  • 1
    Also, you should do the same with `touchesCancelled`. – Aaron Brager Feb 26 '13 at 04:41
  • 2
    I would not recommend doing this. There is no guarantee if the category method or the default method will be called first. This may work but it also means every `UIButton` you have in your project has the potential to clone this behavior (without control). – walkingbrad Apr 10 '15 at 01:11
  • As @walkingbrad writes, bad idea. Categories are not intended to be used for overriding things, and are not reliable for doing so. – uliwitness Apr 30 '16 at 15:14