Какой прием передать событие следующему респонденту в цепочке респондентов? - PullRequest
16 голосов
/ 30 июля 2009

Apple, действительно смешно. Я имею в виду, они говорят, что это работает:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch* touch = [touches anyObject];
    NSUInteger numTaps = [touch tapCount];
    if (numTaps < 2) {
        [self.nextResponder touchesBegan:touches withEvent:event];
   } else {
        [self handleDoubleTap:touch];
   }
}

У меня есть View Controller. Как вы знаете, контроллеры представления наследуются от UIResponder. Этот View Controller создает объект MyView, который наследуется от UIView, и добавляет его как подпредставление к своему собственному представлению.

Итак, имеем:

View Controller> имеет представление (автоматически)> имеет MyView (который является UIView).

Теперь внутри MyView я поместил этот код, как указано выше, в NSLog, который печатает «touch MyView». Но я пересылаю событие следующему респонденту, как и выше. А внутри ViewController у меня есть еще один метод touchesBegan, который просто печатает NSLog а-ля "touch view controller".

Теперь угадайте, что: когда я касаюсь MyView, он печатает «коснулся MyView». Когда я прикасаюсь к внешнему виду MyView, который является видом VC, я получаю «контроллер вида с касанием». Так что оба работают! Но что не работает, так это пересылка события. Потому что теперь, фактически, следующий респондент должен быть контроллером представления, так как между ними нет ничего другого. Но метод обработки событий VC никогда не вызывается, когда я его пересылаю.

% $ &! § !!

Идеи

выяснил странные вещи Следующим ответчиком MyView является представление контроллера представления. Это имеет смысл, потому что MyView является подвидом этого. Но я не изменил этот UIView из контроллера представления. в этом нет ничего обычного. И он не реализует никакой сенсорной обработки событий. Разве сообщение не должно передаваться контроллеру представления? Как я мог просто позволить этому пройти? Если я удаляю код обработки события в MyView, то событие красиво поступает в контроллер представления.

Ответы [ 8 ]

5 голосов
/ 30 июля 2009

Согласно подобному вопросу , ваш метод должен работать.

Это заставляет меня думать, что nextResponder вашего взгляда не на самом деле ViewController, как вы подозреваете.

Я бы добавил NSLog в ваш код пересылки, чтобы проверить, на что в действительности указывает nextResponder:

if (numTaps < 2) {
    NSLog(@"nextResponder = %@", self.nextResponder);
    [self.nextResponder touchesBegan:touches withEvent:event];
}

Вы также можете изменить другие сообщения NSLog, чтобы они выводили тип и информацию об адресе:

NSLog(@"touched %@", self);

touched <UIView 0x12345678>

Это должно помочь вам начать диагностику проблемы.

4 голосов
/ 22 июня 2012

У меня были проблемы с этим, так как мое пользовательское представление было глубже в иерархии представлений. Вместо этого я лазил по цепочке респондента, пока не нашел UIViewController;

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    // Pass to top of chain
    UIResponder *responder = self;
    while (responder.nextResponder != nil){
        responder = responder.nextResponder;
        if ([responder isKindOfClass:[UIViewController class]]) {
            // Got ViewController
            break;
        }
    }
    [responder touchesBegan:touches withEvent:event];
}
2 голосов
/ 16 августа 2015

Если вы посмотрите документацию UIResponder, в описании сказано, что по умолчанию:

UIView реализует этот метод, возвращая объект UIViewController, который управляет им (если он есть) или его суперпредставлением (если его нет);

Если ваше представление не возвращает контроллер представления, тогда оно должно вернуть свое суперпредставление (как вы выяснили).

UIApplication на самом деле имеет метод, который должен точно обрабатывать этот вариант использования:

- (BOOL)sendAction:(SEL)action
                to:(id)target
              from:(id)sender
          forEvent:(UIEvent *)event

Поскольку у вас нет ссылки на цель, вы можете продвигаться вверх по цепочке респондента, установив целевое значение равным nil, так как согласно документации:

Если цель равна нулю, приложение отправляет сообщение первому респонденту, откуда оно переходит в цепочку респондента, пока оно не будет обработано.

Конечно, вы можете получить доступ к UIApplication с помощью метода класса [UIApplication sharedApplication]

Подробнее здесь: UIApplication Documents

Это лишнее, но если вам абсолютно необходим доступ к контроллеру представления, чтобы сделать что-то более сложное, вы можете перемещаться вверх по цепочке респондента вручную, пока не достигнете контроллера представления, вызывая метод nextResponder, пока он не вернется контроллер представления. Например:

UIResponder *responder = self;
while ([responder isKindOfClass:[UIView class]])
    responder = [responder nextResponder];

тогда вы можете просто привести его в качестве контроллера представления и продолжить делать с ним все, что вы хотели:

UIViewController *parentVC = (UIViewController *)responder
2 голосов
/ 23 июля 2012

Для ссылки на контроллер вида необходимо использовать

[[self nextResponder] nextResponder]

потому что [self nextResponder] означает представление контроллера представления, и [[self nextResponder] nextResponder] означает сам контроллер вида, теперь вы можете получить сенсорное событие

2 голосов
/ 30 июля 2009

Да, это должно работать на 100% в общем случае. Я только что попробовал с тестовым проектом, и все, кажется, в порядке.

Я создал приложение на основе вида. Используя Interface Builder, поместите новый UIView в настоящее представление. Затем создайте новый файл с подклассом UIView и выберите только что созданный класс для моего нового представления. (Интерфейсный Разработчик-> Идентификатор класса-> Класс-> MyViewClass)

Добавление функций обработчика касаний как для MyViewClass, так и для UIViewController.

// MyViewClass

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(@"myview touches");
[self.nextResponder touchesBegan:touches withEvent:event];
}

// ViewController

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {  
  NSLog(@"controller touches");
}   

Я вижу оба NSLogs при нажатии MyViewClass. Использовали ли вы Interface Builder и файл XIB при загрузке вашего ViewController или программно устанавливали представление с помощью функции loadView?

1 голос
/ 13 сентября 2018

используйте это

    [super.nextResponder touchesBegan:touches withEvent:event];

перед этой строкой в ​​вашем коде

    [self.nextResponder touchesBegan:touches withEvent:event];

это означает

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
     UITouch* touch = [touches anyObject];
     NSUInteger numTaps = [touch tapCount];
     if (numTaps < 2) {
         [super.nextResponder touchesBegan:touches withEvent:event];
         [self.nextResponder touchesBegan:touches withEvent:event];
     } else {
         [self handleDoubleTap:touch];
     }
}

** Swift 4 Version **

func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
    let touch = touches?.first as? UITouch
    let numTaps: Int? = touch?.tapCount
    if (numTaps ?? 0) < 2 {
        if let aTouches = touches as? Set<UITouch> {
            super.next?.touchesBegan(aTouches, with: event)
            next?.touchesBegan(aTouches, with: event)
        }
    } else {
        handleDoubleTap(touch)
    }
}
1 голос
/ 24 июля 2012

Сегодня я нашел лучший способ исправить это или такой вопрос.

В вашем UITableViewCell вы можете добавить делегата для прослушивания сенсорного события, например:

@protocol imageViewTouchDelegate

-(void)selectedFacialView:(NSInteger)row item:(NSInteger)rowIndex;

@end

@property(nonatomic,assign)id<imageViewTouchDelegate>delegate;

Тогда в вашем UITableViewController: вы можете сделать так:

@interface pressionTable : UITableViewController<facialViewDelegate>

и в файле .m вы можете реализовать этот интерфейс делегата, например:

-(void)selectedFacialView:(NSInteger)row item:(NSInteger)rowIndex{
//TO DO WHAT YOU WANT}

ПРИМЕЧАНИЕ: когда вы инициализируете ячейку, вы должны установить делегата, в противном случае делегат недействителен, вы можете сделать это следующим образом

- (UITableViewCell *)tableView:(UITableView *)tableView 
     cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *CellIdentifier = @"Cell";

pressionCell *cell = (pressionCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    cell = [[pressionCell alloc]initWithFrame:CGRectMake(0, 0, 240, 40)];
}

cell.delegate = self;

}

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

1 голос
/ 21 сентября 2010

Я знаю, что это сообщение старое, но я думал, что id поделиться, потому что у меня был похожий опыт. Я исправил это, установив userInteractionEnabled в NO для представления контроллера представления, который был автоматически создан.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...