OK, I am a little confused.
I have a subclass of UIScrollView, which is my attempt at a horizontally scrolling "table view" like UI element. UIScrollView itself sets up UIGestureRecognizers it uses internally, and it appears to set itself up as the delegate for those UIGestureRecognizers. I also have my own UIGestureRecognizer setup on my horizontal table elements/cells and my own class set as delegate for my own UIGestureRecognizer. Since my class is a subclass of UIScrollView, at runtime, the UIGestureRecognizer delegate calls come to my class for both the UIScrollView in-built UIGestureRecognizers and my own UIGestureRecognizers. A bit of a PITA but we can work around this by passing on the ones we don't care about.
-(BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]])
return NO;
else
{
if ([super respondsToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)])
return [super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
else
return NO;
}
}
The problem is that the check [super respondsToSelector:@selector()]
returns YES, but when I then actually call it return [super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
I get the following exception
2012-08-31 12:02:06.156 MyApp[35875:707] -[MyAppHorizontalImageScroller gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]: unrecognized selector sent to instance 0x21dd50
I would have thought that it should show
-[UIScrollView gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]
But that may be OK. But the problem is that it says that it responds and then doesn't.
The other two UIGestureRecognizer delegate routines work with this code (different selectors obviously).
Thanks for any insight.
[super respondsToSelector:@selector(frobnosticate:)]
doesn't do what you think.It goes to the superclass, gets the implementation of
respondsToSelector:
there, and then runs it on the current object. In other words,super
represents the same object asself
, it just starts the method lookup one step higher in the inheritance tree.So you're running
respondsToSelector:
on this subclass, which replies "YES", but then later trying to getgestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
from the superclass, which doesn't have it.To check instances of the immediate superclass, you would use
instancesRespondToSelector:
, as jjburka recommend, but I would suggest[self superclass]
as the subject of that, like so:which avoids hardcoding class names.
When you call
This is going to the superclass and running its implementation of
respondsToSelector
. This looks at the instance (in this case your custom scroll view) and determines if it responds to that selector or not.When you call
You try to send a message using the superclass implementation of that method, which in this case does not exist, causing the crash.
Looks like jjburka got to it first - you need to call
Also, it won't crash if you use
-[super performSelector:]
from unrecognized selector - perform selector gets the instances implementation of the selector. It will crash from infinite recursion.After looking at the other answers, the best solution is to use
[[MapControllerSublcass1 superclass] instancesRespondToSelector:_cmd]
. If you use what is recommended above, something like[BaseClass instancesRespondToSelector:_cmd]
, you run into the problem of changing your class hierarchy and accidentally forgetting to changeBaseClass
to the new superclass or your subclass.[[self superclass] instancesRespondToSelector:...]
is incorrect as explained above in the comments and it actually says so on Apple's documentation (See respondsToSelector: in NSObjct). It only works when you have 1 level of subclassing, so it gives you the illusion that it is an actual solution. I fell for it.And
[[super class] instancesRespondToSelector:...]
doesn't work and is the whole point of this SO question.For example, I have a
BaseMapController
that implements some of the methods inMKMapViewDelegate
, but it doesn't implementmapView:regionWillChangeAnimated:
.MapControllerSublcass1
inherits fromBaseMapController
. AndMapControllerSubclass2
inherits fromMapControllerSublcass1
.In my code I have something like this, and it works fine.
MapControllerSublcass1.m:
MapControllerSubclass2.m:
Unless you override responds to selector in your class you will be using the default implementation when you call super which will check the current instance. If you want to see if a instance of a type of object responds to a selector use
+(BOOL)instancesRespondToSelector:(SEL)aSelector;
This will check the object and its parent objects. So in your case you want to the following:
As a summary for someone with the same case, there are two problems in the original question:
instancesRespondToSelector:
.Putting it together this gives: