I am handling touches for a couple of my UI components in my view controller (custom subclass of UIViewController). It has methods touchesBegan:withEvent:
, touchesMoved:withEvent:
, and touchesEnded:withEvent:
. It was working fine. Then I added a scroll view (UIScrollView) as the top view in the hierarchy.
Now my touch handlers on the view controller don't work. They don't get called. The interesting thing is, I have various other UI components within the scroll view that do work. Some are buttons, some are custom views that define their own touchesBegan:withEvent:
, etc. The only thing that doesn't work is the touch handlers on the view controller.
I thought maybe it's because the scroll view is intercepting those touches for its own purposes, but I subclassed UIScrollView and just to see if I could get it to work I am returning YES
always from touchesShouldBegin:withEvent:inContentView:
and NO
always from touchesShouldCancelInContentView:
. Still doesn't work.
If it makes a difference my view controller is within a tab bar controller, but I don't think it's relevant.
Has anyone had this problem and have a ready solution? My guess is the scroll view monkeys up the responder chain. Can I monkey it back? I guess if I can't figure anything else out I'll make the top level view under my scroll view be a custom view and forward the messages on to the view controller, but seems kludgy.
create a subclass of UIScrollView class and override the touchesBegan: and other touch methods as follows:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// If not dragging, send event to next responder
if (!self.dragging){
[self.nextResponder touchesBegan: touches withEvent:event];
}
else{
[super touchesBegan: touches withEvent: event];
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
// If not dragging, send event to next responder
if (!self.dragging){
[self.nextResponder touchesMoved: touches withEvent:event];
}
else{
[super touchesMoved: touches withEvent: event];
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
// If not dragging, send event to next responder
if (!self.dragging){
[self.nextResponder touchesEnded: touches withEvent:event];
}
else{
[super touchesEnded: touches withEvent: event];
}
}
Well this worked, but I'm not sure I can "get away with it", since nextResponder is not one of the UIView methods you're "encouraged" to override in a subclass.
@interface ResponderRedirectingView : UIView {
IBOutlet UIResponder *newNextResponder;
}
@property (nonatomic, assign) IBOutlet UIResponder *newNextResponder;
@end
@implementation ResponderRedirectingView
@synthesize newNextResponder;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (UIResponder *)nextResponder {
return self.newNextResponder;
}
- (void)dealloc
{
[super dealloc];
}
@end
Then in Interface Builder I made the direct subview of the scroll view one of these, and hooked up its newNextResponder to skip the scrollview and point directly to the view controller.
This works too, replacing the override of nextResponder with these overrides:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.newNextResponder touchesBegan:touches withEvent:event];
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[self.newNextResponder touchesMoved:touches withEvent:event];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self.newNextResponder touchesEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self.newNextResponder touchesCancelled:touches withEvent:event];
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With