UITableViewCell пропущен в цепочке ответчиков

Я пытаюсь вызвать событие в subview 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");
}

И чтобы проверить, почему это не работает, я добавил метод someAction: в ViewController, и ViewController - это тот, который завершает обработку события, которое пузырится из подматрицы ячейки таблицы, хотя Ячейка должна справиться с этим. Я проверил, что ячейка находится в цепочке ответчиков, и я проверил, что любые представления в цепочке ответчиков, как над, так и под ячейкой, будут реагировать на событие, если они реализуют метод someAction:.

Что здесь происходит?

Здесь проект, который показывает его https://github.com/keithnorm/ResponderChainTest Является ли это ожидаемое поведение каким-то образом? Я не нашел никаких документов, в которых говорится, что UITableViewCell обрабатывается иначе, чем другие файлы UIResponder.

Ответ 1

Кажется, что ячейка запрашивает разрешение для своего табличного представления. Чтобы изменить это, вы можете, конечно, переопределить

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

Ответ 2

Я пришел к выводу, что это либо ошибка, либо недокументированное намеренное поведение. Во всяком случае, я закончил грубую силу, установив ее, ответив на событие в подвью, а затем вручную распространив сообщение на цепочку ответчиков. Что-то вроде:

- (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.

Я по-прежнему приветствую любые другие идеи, если кто-то еще это оценит, но это лучшее, что у меня есть сейчас.

Ответ 3

сделайте это

    @implementation ContentView

   // uncomment this to see event caught by the cell 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: метод вызывается

Ответ 4

Вы можете изменить код в View.m как

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

к

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

Ответ 5

Я думаю, что это самое простое решение. Если вы не укажете цель, событие автоматически запустит цепочку ответчиков.

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