【问题标题】:UITableViewCell skipped in responder chainUITableViewCell 在响应者链中跳过
【发布时间】:2014-02-15 02:31:51
【问题描述】:

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

基本上:

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

[self.button addTarget:nil action:@selector(someAction:) events:UIControlEventTouchUpInside]

SomeCustomCell.m

- (void)someAction:(id)sender {
     NSLog(@"cool, the event bubbled up to the cell");
}

为了测试为什么这不起作用,我在 ViewController 上添加了 someAction: 方法,而 ViewController 是最终处理从表格视图单元子视图冒泡的事件的方法,即使细胞应该处理它。我已经检查了 Cell 是否在响应者链上,并且我已经验证了响应者链上的任何视图在单元上方和下方都将响应该事件,如果它们实现了 someAction: 方法。

这到底是怎么回事?

这是一个显示它的项目https://github.com/keithnorm/ResponderChainTest 这是某种预期的行为吗?我没有找到任何说明 UITableViewCell 的处理方式与其他 UIResponder 不同的文档。

【问题讨论】:

  • 这很奇怪!我发现响应者链是:ContentView->UITableViewCellContentView->UITableViewCellScrollView->TableCell->UITableViewWrapperView->UITableView->View->ViewController->UIWindow->UIApplication->AppDelegate。我是通过UIResponder * res = sender ; while (res) { res = [res nextResponder] ; NSLog(@"%@", [res class]) ; } 找到的。我会关注这个问题。
  • 是的,看起来应该可以了,对吧?感谢您检查并确认它对您来说也很奇怪:)
  • 是的,我下载了您的项目并希望它可以工作但失败了。我也很困惑为什么它会跳过TableCell
  • 所以你的目标是你只想触发 TableCell 的 customEventFired: 方法。对吗?
  • 我想让事件在子视图中触发,但被 TableCell 捕获。

标签: ios uitableview uikit uiresponder responder-chain


【解决方案1】:

这样做

    @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: 方法被调用

【讨论】:

  • 谢谢,但我想做的是专门利用响应者链。我的单元格没有像您的示例那样直接访问按钮,我想保持这种方式以保持这些对象解耦。 Point 是在子视图中触发的事件,理论上应该发送到父视图。这就像 JavaScript 中的事件委托/冒泡。
  • 好吧,对不起,我没有下载你的代码,只是直接发布了我的答案,所以我现在知道了,你不要在 contentView 本身中添加按钮,然后你会得到单元格子视图的响应者
【解决方案2】:

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

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

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

【讨论】:

  • 如果子视图有对其父单元格的引用,我可以这样做,但这也破坏了我试图维护的解耦。子视图几乎不应该知道它的超级视图,IMO。
【解决方案3】:

我得出的结论是,这要么是错误,要么是未记录的预期行为。无论如何,我最终通过在子视图中响应事件然后手动将消息传播到响应者链上来暴力修复它。比如:

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

我还更新了我的演示项目,以展示我如何使用这个“修复”https://github.com/keithnorm/ResponderChainTest

如果其他人能解决这个问题,我仍然欢迎任何其他想法,但这是我目前最好的。

【讨论】:

    【解决方案4】:

    单元格似乎向其表格视图请求许可。要改变它,你当然可以覆盖

    - (BOOL)canPerformAction:(SEL)action withSender:(id)sender
    {
        return [self respondsToSelector:action];
    }
    

    斯威夫特 3、4、5:

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

    【讨论】:

    • 我可以确认这是可行的,尽管我不确定“单元格似乎要求其表格视图获得许可”是对这种行为的正确解释。表视图与它有什么关系?这是否记录在某处?
    • 表格视图询问它的委托 -tableView:canPerformAction:forRowAtIndexPath:withSender:,但前提是它们是复制:或粘贴:)。该部分已记录在案。这就是响应者链无法按预期工作的原因:单元劫持操作以让表视图委托执行菜单验证。
    • 有趣,感谢您的回答!我在github.com/keithnorm/ResponderChainTest 更新了我的示例项目,以表明这确实有效。
    【解决方案5】:

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

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

    【讨论】:

    • QA 询问有关从 UIButton 转发到 UITableViewCell 的操作。只有当第一响应者存在时,您的解决方案才会向第一响应者发送操作。唯一可以成为第一响应者的 UIKit 组件是 UITextField 和 UITextView,这不是问题中的情况。
    • @Adobels 这实际上是您传播的错误信息。许多关键对象也是响应者,包括 UIApplication 对象、UIViewcontrollers 和 UIViews。
    • 您将下一个响应者与第一响应者混淆了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多