Подтвердить что ты не робот

UIView -hitTest: withEvent: вызывается три раза?

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

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

Однако, когда я NSLog в этот метод overriden перед вызовом [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] ---

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

Спасибо заранее!

4b9b3361

Ответ 1

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

Вот как я определяю, какой 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
  }
}

Ответ 2

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

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

В общем, 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];
}

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

Ответ 3

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

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

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

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

Из Ссылка на класс UIView.

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