UIScrollView prevents touchesBegan, touchesMoved, touchesEnded on view controller
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];
}
"It was working fine. Then I added a scroll view (UIScrollView) as the top view in the hierarchy."
is your scrollview on top of your contentview that contains items?
all your components should be in the scrollview and not the view behind the scrollview
user1085093's answer worked for me. Once you move the touch more than a small amount, however, it then gets interpreted as a Pan Gesture.
I overcame this by altering the behaviour of the Pan Gesture recogniser so it requires two fingers:
-(void)awakeFromNib
{
NSArray *gestureRecognizers = self.gestureRecognizers;
UIGestureRecognizer *gestureRecognizer;
for (gestureRecognizer in gestureRecognizers) {
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
UIPanGestureRecognizer *pgRecognizer = (UIPanGestureRecognizer*)gestureRecognizer;
pgRecognizer.minimumNumberOfTouches = 2;
}
}
}
The touchesBegan:
etc methods will NEVER be called in a UIScrollView
because it is a subclass of UIView
and overrides these methods. check out the different UIScrollView
methods available here. The work-around will depend on what you want to implement.
精彩评论