21

好吧,我有点困惑。

我有一个 UIScrollView 的子类,这是我尝试像 UI 元素一样水平滚动的“表格视图”。UIScrollView 本身设置了它在内部使用的 UIGestureRecognizers,并且它似乎将自己设置为那些 UIGestureRecognizers 的委托。我的水平表格元素/单元格上也有我自己的 UIGestureRecognizer 设置,我自己的类设置为我自己的 UIGestureRecognizer 的委托。由于我的类是 UIScrollView 的子类,因此在运行时,UIGestureRecognizer 委托调用会针对 UIScrollView 内置 UIGestureRecognizers 和我自己的 UIGestureRecognizers 进入我的类。有点像 PITA,但我们可以通过传递我们不关心的那些来解决这个问题。

-(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;
        }
}

问题是检查[super respondsToSelector:@selector()]返回YES,但是当我实际调用它时,return [super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];我得到以下异常

2012-08-31 12:02:06.156 MyApp[35875:707]-[MyAppHorizo​​ntalImageScrollergestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]: 无法识别的选择器发送到实例 0x21dd50

我原以为它应该显示

-[UIScrollView 手势识别器:shouldRecognizeSimultaneouslyWithGestureRecognizer:]

但这可能没问题。但问题是它说它响应然后没有。

其他两个 UIGestureRecognizer 委托例程使用此代码(显然不同的选择器)。

感谢您的任何见解。

4

5 回答 5

26

除非您在类中覆盖对选择器的响应,否则您将在调用 super 时使用默认实现,该实现将检查当前实例。如果您想查看某类对象的实例是否响应选择器,请使用+(BOOL)instancesRespondToSelector:(SEL)aSelector;

这将检查对象及其父对象。因此,在您的情况下,您需要以下内容:

[UIScrollView instancesRespondToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]
于 2012-08-31T18:24:04.280 回答
17

[super respondsToSelector:@selector(frobnosticate:)]不按你的想法做。

它转到超类,获取那里的实现respondsToSelector:,然后在当前对象上运行它。换句话说,super表示与 相同的对象self,它只是开始在继承树中更高一级的方法查找。

所以你respondsToSelector:在这个子类上运行,它回答“是”,但后来试图gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:从没有它的超类中获取。

要检查直接超类的实例,您可以使用instancesRespondToSelector:jjburka 推荐的方法,但我建议将[self superclass]其作为主题,如下所示:

[[self superclass] instancesRespondToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)];

这避免了硬编码类名。

于 2012-08-31T18:38:32.160 回答
5

在查看其他答案后,最好的解决方案是使用[[MapControllerSublcass1 superclass] instancesRespondToSelector:_cmd]. 如果您使用上面推荐的方法,例如[BaseClass instancesRespondToSelector:_cmd],您会遇到更改类层次结构的问题,并且不小心忘记更改BaseClass为新的超类或子类。

[[self superclass] instancesRespondToSelector:...]正如上面在评论中所解释的那样是不正确的,它实际上在Apple 的文档中是这样说的(参见 respondsToSelector: in NSObjct)。它仅在您具有 1 级子类化时才有效,因此它给您一种错觉,即它是一个实际的解决方案。我爱上了它。

并且[[super class] instancesRespondToSelector:...]不起作用,这是这个 SO 问题的重点。

例如,我有一个BaseMapController实现了 中的一些方法MKMapViewDelegate,但它没有实现mapView:regionWillChangeAnimated:. MapControllerSublcass1继承自BaseMapController。并MapControllerSubclass2继承自MapControllerSublcass1.

在我的代码中,我有类似的东西,它工作正常。

MapControllerSublcas1.m

- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
  if ([[MapControllerSublcass1 superclass] instancesRespondToSelector:_cmd]) {
    [super mapView:mapView regionWillChangeAnimated:animated];
  }
}

MapControllerSubclass2.m

- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
  if ([[MapControllerSubclass2 superclass] instancesRespondToSelector:_cmd]) {
    [super mapView:mapView regionWillChangeAnimated:animated];
  }
}
于 2013-07-25T17:34:03.157 回答
0

作为一个有相同情况的人的总结,原来的问题有两个问题:

  1. 检查超类是否响应该选择器,正如@jjburka 建议的那样,最好使用instancesRespondToSelector:.
  2. 实际上在超类上调用该方法而编译器不会抱怨,即使它是在私有头文件中声明的。这可以通过在一个类别中重新声明它来巧妙地实现(参见这个问题)。

把它放在一起给出:

// … In subclass implementation file
@interface UIScrollView () <UIGestureRecognizerDelegate> @end

// … In gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
if ([UIScrollView instancesRespondToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]) {
    return [super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
}
于 2013-12-05T00:27:54.690 回答
0

你打电话时

[super respondsToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]

这将转到超类并运行其实现respondsToSelector。这会查看实例(在本例中是您的自定义滚动视图)并确定它是否响应该选择器。

你打电话时

[super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];

您尝试使用该方法的超类实现发送消息,在这种情况下该方法不存在,从而导致崩溃。

看起来 jjburka 先到了 - 你需要打电话

[UIScrollView instancesRespondToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]

此外,如果您使用来自无法识别的选择器,它也不会崩溃-[super performSelector:]- 执行选择器获取选择器的实例实现。它将因无限递归而崩溃。

于 2012-08-31T18:36:07.213 回答