Ios 响应程序链中跳过UITableViewCell

Ios 响应程序链中跳过UITableViewCell,ios,uitableview,uikit,uiresponder,responder-chain,Ios,Uitableview,Uikit,Uiresponder,Responder Chain,我试图在UITableViewCell的子视图中触发一个事件,让它在响应程序链中冒泡,并由自定义UITableViewCell子类处理 基本上: SomeView.m(它是UITableViewCell的子视图) SomeCustomCell.m - (void)someAction:(id)sender { NSLog(@"cool, the event bubbled up to the cell"); } 为了测试为什么不起作用,我在ViewController上添加了some

我试图在
UITableViewCell
的子视图中触发一个事件,让它在响应程序链中冒泡,并由自定义
UITableViewCell
子类处理

基本上:

SomeView.m(它是
UITableViewCell
的子视图)

SomeCustomCell.m

- (void)someAction:(id)sender {
     NSLog(@"cool, the event bubbled up to the cell");
}
为了测试为什么不起作用,我在ViewController上添加了
someAction:
方法,ViewController最终处理从表视图单元格子视图冒泡出来的事件,即使单元格应该处理它。我已经检查了该单元格是否位于响应器链上,并且我已经验证了如果响应器链上的任何视图实现了
someAction:
方法,则该单元格上方和下方的视图都将响应该事件

这到底是怎么回事

这是一个项目,它表明这是某种预期的行为?我没有发现任何文档说明UITableViewCell的处理方式与其他UIResponder的有任何不同。

这样做吗

    @implementation ContentView

   // uncomment this to see event caught by the cell's subview

  - (id)initWithFrame:(CGRect)frame
 {
     self = [super initWithFrame:frame];
    if(self)
   {

    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    [button setTitle:@"Click" forState:UIControlStateNormal];
    [button setBackgroundColor:[UIColor blueColor]];
    [button addTarget:self action:@selector(customEventFired:) forControlEvents:UIControlEventTouchUpInside];
    button.frame = CGRectMake(4, 5, 100, 44);
    [self addSubview:button];
  }

    return self;
}

 - (void)customEventFired:(id)sender
{
     UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Event Triggered in cell subview" message:@"so far so good..." delegate:nil cancelButtonTitle:@"cancel" otherButtonTitles:nil, nil];
    [alertView show];
 }

@end

现在
customEventFired:
method-get-called

您可以将View.m中的代码更改为

      [button addTarget:nil action:@selector(customEventFired:) forControlEvents:(1 << 24)];

[button addTarget:nil action:@selector(CustomEventFireed:)forControlEvents:(1我得出结论,这要么是一个bug,要么是未记录的预期行为。无论如何,我最终通过在子视图中响应事件,然后在响应器链上手动传播消息来强制修复它。类似于:

- (void)customEventFired:(id)sender {
  UIResponder *nextResponder = self.nextResponder;
  while (nextResponder) {
    if ([nextResponder respondsToSelector:@selector(customEventFired:)]) {
      [nextResponder performSelector:@selector(customEventFired:) withObject:sender];
      break;
    }
    nextResponder = nextResponder.nextResponder;
  }
}
我还更新了我的演示项目,以展示我如何使用这个“修复”


如果有人知道了这一点,我仍然欢迎任何其他想法,但这是我目前最好的想法。

单元格似乎向其表视图请求权限。若要更改,当然可以覆盖

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    return [self respondsToSelector:action];
}
Swift 3、4、5:

override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    return self.responds(to: action)
}

我认为这是最简单的解决方案。如果不指定目标,事件将自动在响应程序链上冒泡

[[UIApplication sharedApplication]sendAction:@selector(customAction:) to:nil from:self forEvent:UIEventTypeTouches];

谢谢,但我想做的是特别利用响应器链。我的单元格没有像您的示例那样直接访问按钮,我希望保持这种方式以保持这些对象的解耦。点是在子视图中触发的事件,理论上应该发送到superview。它类似于事件委派/bubb很抱歉,我没有下载你的代码,只是直接发布了我的答案,所以我现在明白了,为什么不在contentView中添加按钮,然后将响应程序添加到单元格的子视图?这很奇怪!我发现响应程序链是:
contentView->UITableViewCellContentView->UITableViewCellScrollView->TableCell->UITableViewWrapperView->UITableView->View->ViewController->UIWindow->UIApplication->AppDelegate
。我通过
UIResponder*res=sender;而(res){res=[res nextResponder];NSLog(@“%@,[res class]);}找到了它
。我会回答这个问题。是的,它似乎应该会起作用,对吗?感谢您查看并确认它对您来说也很奇怪:)是的,我下载了你的项目,希望它能工作,但失败了。我还不明白为什么它会跳过
TableCell
。所以你的目标是你只想在TableCell上触发customEventFired:方法。对吗?我想在子视图中触发事件,但被TableCell捕获。如果子视图引用了其父视图,我可以单元格,但这也打破了我试图维护的解耦。在我看来,子视图几乎永远不应该知道它的超级视图。我可以确认这是可行的,尽管我不确定“单元格似乎向它的表视图请求许可”是此行为的正确解释。表视图与此有何关系?是否有文档记录?表视图正在询问它的委托-表视图:canPerformAction:forRowAtIndexPath:withSender:,但仅当它们是copy:或paste:)。该部分已被记录。这就是响应程序链不能作为exp工作的原因ected:单元格劫持操作以让表视图代理执行菜单验证。很有趣,感谢您的回答!我在更新了我的示例项目,以表明这确实有效。QA正在询问从UIButton转发到UITableViewCell的操作。您的解决方案将仅在该操作有效时向第一响应者发送操作xist。唯一可以成为第一个响应者的UIKit组件是UITextField和UITextView,这在问题中不是一个案例。@a这实际上是传播的错误信息。许多关键对象也是响应者,包括UIApplication对象、UIViewController和UIViews。您会将下一个响应者与第一个响应者混淆德。
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    return self.responds(to: action)
}
[[UIApplication sharedApplication]sendAction:@selector(customAction:) to:nil from:self forEvent:UIEventTypeTouches];