Какая уловка передала событие следующему ответчику в цепочке ответчиков?
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. Как вы знаете, View Controllers наследуются от UIResponder. Этот диспетчер представлений создает объект MyView, который наследует UIView, и добавляет его в качестве подчиненного к нему собственного представления.
Итак, мы имеем:
View Controller > имеет вид (автоматически) > имеет MyView (это UIView).
Теперь внутри MyView я помещаю этот код, как показано выше, с NSLog, который печатает "коснулся MyView". Но я передаю это событие следующему ответчику, как и выше. И внутри ViewController у меня есть еще один метод touchhesBegan, который просто печатает NSLog a la "touched view controller".
Теперь угадайте, что: Когда я касаюсь MyView, он печатает "коснулся MyView". Когда я касаюсь вне MyView, который является видом VC, я получаю "сенсорный контроллер просмотра". Поэтому оба работают! Но что не работает, это пересылка события. Потому что теперь на самом деле следующим ответчиком должен быть контроллер вида, так как между ними нет ничего другого. Но метод обработки событий VC никогда не вызывается, когда я его пересылаю.
$% &! §!!
Идеи, ребята?
Выяснил странный материал
MyView next responder - это вид контроллера вида. Это имеет смысл, потому что MyView является подходом к этому. Но я не модифицировал этот UIView из контроллера представления. это ничего обычая. И он не реализует обработку сенсорных событий. Не следует ли передать сообщение в контроллер вида? Как я могу просто позволить этому пройти? Если я удалю код обработки событий в MyView, то событие прекрасно поступит в контроллер вида.
Ответы
Ответ 1
В соответствии с похожим вопросом ваш метод должен работать.
Это заставляет меня думать, что ваш взгляд nextResponder
на самом деле не является ViewController
, как вы подозреваете.
Я бы добавил быстрый NSLog
в ваш код пересылки, чтобы проверить, что на самом деле указывает nextResponder
:
if (numTaps < 2) {
NSLog(@"nextResponder = %@", self.nextResponder);
[self.nextResponder touchesBegan:touches withEvent:event];
}
Вы также можете изменить другие сообщения NSLog, чтобы они отображали информацию о типе и адресе:
NSLog(@"touched %@", self);
touched <UIView 0x12345678>
Это должно помочь вам начать диагностику проблемы.
Ответ 2
У вас была проблема с этим, так как мой пользовательский вид был глубже в иерархии представлений. Вместо этого я поднялся на цепочку ответчиков, пока не найдет 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];
}
Ответ 3
Да, он должен работать на 100% в общем случае. Я только что пробовал с тестовым проектом, и все, кажется, в порядке.
Я создал приложение на основе View. Используя Interface Builder добавьте новый UIView в настоящий вид. Затем создайте новый файл с подклассом UIView и выберите только созданный класс для моего нового представления. (Interface Builder- > Class identity- > Class- > 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?
Ответ 4
Чтобы ссылаться на контроллер вида, вам нужно использовать
[[self nextResponder] nextResponder]
поскольку [self nextResponder]
означает просмотр контроллера вида,
и [[self nextResponder] nextResponder]
означает сам контроллер представления, теперь вы можете получить событие касания
Ответ 5
Если вы посмотрите документацию UIResponder
, в описании указано, что по умолчанию:
UIView реализует этот метод, возвращая объект UIViewController, который управляет им (если он есть) или его супервизор (если это не так);
Если ваше представление не возвращает контроллер вида, оно должно возвращать его супервизор (как вы выяснили).
У UIApplication действительно есть метод, который должен обрабатывать этот прецедент точно:
- (BOOL)sendAction:(SEL)action
to:(id)target
from:(id)sender
forEvent:(UIEvent *)event
Поскольку у вас нет ссылки на цель, вы можете проложить путь к цепочке ответчиков, установив целевое значение как nil
, поскольку в соответствии с документацией:
Если цель равна нулю, приложение отправляет сообщение первому ответчику, откуда он прогрессирует цепочку ответчиков до тех пор, пока не будет обработано.
Конечно, вы можете получить доступ к UIApplication с помощью метода класса [UIApplication sharedApplication]
Дополнительная информация здесь: Документы UIApplication
Это лишний раз, но если вам абсолютно необходим доступ к контроллеру представления, чтобы сделать что-то более сложное, вы можете вручную проработать цепочку ответов, пока не дойдете до контроллера вида, вызвав метод nextResponder
, пока он не вернется контроллер просмотра. Например:
UIResponder *responder = self;
while ([responder isKindOfClass:[UIView class]])
responder = [responder nextResponder];
тогда вы можете просто использовать его в качестве своего контроллера представления и продолжать делать все, что вы хотели с ним сделать:
UIViewController *parentVC = (UIViewController *)responder
Ответ 6
Я знаю, что это сообщение устарело, но я думал, что его используют, потому что у меня был подобный опыт. Я исправил его, установив для параметра userInteractionEnabled значение NO для представления автоматически созданного контроллера представления.
Ответ 7
Сегодня я нашел лучший способ исправить это или вопрос вроде этого.
В вашем 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 старый, но я все же добавляю свой метод, чтобы исправить вопрос, поэтому, когда люди сталкиваются с одной и той же проблемой, они получат помощь от моего ответа.