UIView's -hitTest: withEvent: вызывается три раза? - PullRequest
13 голосов
/ 29 ноября 2010

Я пытаюсь перехватить любую активность (например, касания), которая происходит внутри всего моего приложения.

Другими словами, я пытаюсь получать уведомления о любом событии касания, которое происходит в моем основном UIView, содержащем остальные мои элементы управления. Для этого я подумал, что метод UIView -hitTest: withEvent: был хорошим решением.

Однако, когда я NSLog в этот переопределенный метод перед вызовом [super hitTest: ... withEvent: ...], я вижу, что он вызывается 3 раза для любого касания, которое я делаю, и я не вижу никакой разницы событие, которое я получаю каждый раз, когда оно вызывается.

Вот как реализован метод в главном окне моего приложения:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    NSLog(@"hitTest:withEvent called :");
    NSLog(@"Event: %@", event);
    NSLog(@"Point: %@", NSStringFromCGPoint(point));
    NSLog(@"Event Type: %d", event.type);
    NSLog(@"Event SubType: %d", event.subtype);
    NSLog(@"---");

    return [super hitTest:point withEvent:event];
}

И вот что я NSLog для одного прикосновения в этом представлении:

2010-11-29 14:09:26.892 Application[68818:207] hitTest:withEvent called :
2010-11-29 14:09:26.892 Application[68818:207] Event: <UITouchesEvent: 0x5716d60> timestamp: 37935.2 touches: {(
)}
2010-11-29 14:09:26.892 Application[68818:207] Point: {173, 498}
2010-11-29 14:09:26.892 Application[68818:207] Event Type: 0
2010-11-29 14:09:26.892 Application[68818:207] Event SubType: 0
2010-11-29 14:09:26.893 Application[68818:207] ---
2010-11-29 14:09:26.893 Application[68818:207] hitTest:withEvent called :
2010-11-29 14:09:26.893 Application[68818:207] Event: <UITouchesEvent: 0x5716d60> timestamp: 37935.2 touches: {(
)}
2010-11-29 14:09:26.893 Application[68818:207] Point: {173, 498}
2010-11-29 14:09:26.893 Application[68818:207] Event Type: 0
2010-11-29 14:09:26.893 Application[68818:207] Event SubType: 0
2010-11-29 14:09:26.893 Application[68818:207] ---
2010-11-29 14:09:26.893 Application[68818:207] hitTest:withEvent called :
2010-11-29 14:09:26.894 Application[68818:207] Event: <UITouchesEvent: 0x5716d60> timestamp: 37944.9 touches: {(
)}
2010-11-29 14:09:26.894 Application[68818:207] Point: {173, 498}
2010-11-29 14:09:26.894 Application[68818:207] Event Type: 0
2010-11-29 14:09:26.894 Application[68818:207] Event SubType: 0
2010-11-29 14:09:26.894 Application[68818:207] ---

Как я могу сделать разницу между этими тремя уведомлениями, чтобы вызвать действие, которое я хочу совершить только один раз за одно касание?

Заранее спасибо!

Ответы [ 4 ]

10 голосов
/ 27 декабря 2010

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

Вот как я определяю, какой хит-тест обрабатывать:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  NSTimeInterval system = [[NSProcessInfo processInfo] systemUptime];

  if (system - event.timestamp > 0.1) {
    // not the event we were interested in
  } else {
    // use this call
  }
}
8 голосов
/ 04 апреля 2012

Если это все еще проблема для вас.

Я нашел несколько примеров и обсуждений на эту тему, но правильное решение довольно простое.

Как правило, hittest вызывается три раза в UIView или UIScrollView - это результат обхода иерархии представления.

Довольно простое и всегда подходящее для меня решение:

реализовать функцию hittest в представлении, которое вы реализовали

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {

для меня - все три вызова функций всегда имеют одинаковую позицию - так что просто сохраните местоположение в вашем местном классе.

в файле .h:

@interface MyScrollView : UIScrollView {
    CGPoint touchLocation_;
}

@property (nonatomic, readonly) CGPoint touchLocation;

в .m файле

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    return [super hitTest:point withEvent:event];
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
// The point check you need    
    if(point.y != self.touchLocation.y){
        touchLocation_ = point;
    // The function call you need - just called once.
    }
    return [super pointInside:point withEvent:event];
}

Это решение хорошо работает для меня в нескольких проектах.

2 голосов
/ 29 ноября 2010

Количество полученных вами ответов на события зависит от иерархии представлений.

Этот метод пересекает вид иерархия, отправив pointInside: withEvent: сообщение каждому подпредставление, чтобы определить, какое подпредставление должен получить сенсорное событие. Если pointInside: withEvent: возвращает YES, тогда иерархия подпредставления пройденного; в противном случае его ветвь иерархия представления игнорируется. Вы редко нужно вызывать этот метод себя, но вы можете переопределить его скрыть сенсорные события от подпредставлений.

Этот метод игнорирует объекты просмотра, которые скрыты, что отключили пользователя взаимодействие или иметь альфа-уровень менее 0,01 Этот метод не принять во внимание содержание представления при определении попадания. Таким образом, мнение все еще можно вернуть, даже если указанная точка находится в прозрачной часть содержимого этого представления.

Точки, которые находятся за пределами получателя границы никогда не сообщаются как хиты, даже если они на самом деле лежат в одном из подпредставлений получателя. Это может произойдет, если текущий вид Для свойства clipsToBounds установлено значение NO и затронутое подпредставление расширяется за пределами представления.

Из справочника классов UIView .

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

0 голосов
/ 05 апреля 2019

Этот вопрос задавался годами, но я хочу ответить на него:

Система вызывает hitTest и pointInside несколько раз, чтобы исправить положение, и каждый раз, когда стек вызовов отличается, см. официальный ответ от apple.

...