UIView hitTest: с событием: вызовы поступают по три

#objective-c #cocoa-touch #ios #uiscrollview

#objective-c #cocoa-touch #iOS #uiscrollview

Вопрос:

У меня есть UIScrollView без вложенных представлений. Когда я перетаскиваю вид прокрутки, он hitTest:withEvent: вызывается три раза, и в событии никогда не происходит никаких касаний. Почему это (я видел другой пост, но, похоже, у него нет conclusion)? Вот журнал просмотра прокрутки hitTest... и pointInside:withEvent:

2011-05-11 20:12:37.472 MyApp[10909:707] тест попадания для UIScrollView 119.500000, 102.000000, временная метка: 357978 касания: {()}
2011-05-11 20:12:37.475 MyApp[10909:707] Указывает на UIScrollView 119.500000, 102.000000, временная метка: 357978 касания: {()} 
2011-05-11 20:12:37.477 MyApp [10909:707] тест попадания для UIScrollView 119.500000, 102.000000, временная метка: 357978 касания: {()}
2011-05-11 20:12:37.479 MyApp[10909:707] Указывает на UIScrollView 119.500000, 102.000000, временная метка: 357978 касания: {()} 
2011-05-11 20:12:37.481 MyApp[10909:707] тест попадания для UIScrollView 119.500000, 102.000000, временная метка: 358021 касания: {()}
2011-05-11 20:12:37.482 MyApp[10909:707] Указывает на UIScrollView 119.500000, 102.000000, временная метка: 358021 касания: {()} 
2011-05-11 20:12:37.484 MyApp[10909:707] Указывает на UIScrollView 119.500000, 396.000000, временная метка: 358021 касания: {()} 

Комментарии:

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

2. Не уверен, что это ответ, поэтому я опубликую его здесь … предполагая, что ваша иерархия такова: UIWindow —> UIView от UIViewController -> UIScrollView, возможно ли, что вы получаете тест на попадание для каждого представления «hittable» в иерархии? В документации указано, что pointInside вызывается ниже по иерархии. Добавьте UIView в свой вид прокрутки и посмотрите, вызовете ли вы его 4 раза. Другая идея заключается в том, что это дает вам 3, чтобы вы могли получить среднее значение.

Ответ №1:

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

Если вы хотите отслеживать события касания, вам следует переопределить touchesBegan: и друзей.

Ответ №2:

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

1. Сохраните тесты попадания в массив и сравните их, чтобы вы могли вернуться, когда результат будет одинаковым, чтобы предотвратить его вызов 3 раза подряд.

2. Вместо hittest используйте touchesBegan, touchesCancelled, touchesEnded и touchesMoved следующим образом:

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch * touch = [[event allTouches] anyObject];
    CGPoint touchLocation = [touch locationInView:touch.view];
    NSLog(@"%f - %f", touchLocation.x, touchLocation.y);
}
  

Я использовал решение touchesBegan и т.д., И оно работает просто великолепно 🙂

Ответ №3:

Я не знаю наверняка, почему, но вот некоторые дополнительные сведения по этому вопросу:

  • Похоже, это не относится конкретно к UIScrollView. Я запустил тест с UIView — те же результаты.
  • Кажется, что он вызывается три раза в корневом представлении независимо от иерархии представлений или возвращаемого значения.
  • Событие, переданное в hitTest, в основном пустое для первых двух вызовов (но точка всегда действительна); событие имеет действительную временную метку, но нет информации о касании для третьего вызова.
  • Похоже, что UIResponder методы вызываются только после завершения всех трех вызовов hitTest. Например, touchesBegan:withEvent: не будет вызываться до конца.
  • Все три вызова исходят из разных точек в (я полагаю, с закрытым исходным кодом) функции UIApplicationHandleEvent, основанной на трассировках стека.

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

Другая идея заключается в том, что код просто написан неэффективно. Это кажется менее вероятным, но, основываясь на этой информации, это возможно.

Комментарии:

1. Я настроил проект с видом прокрутки, расположенным в представлении VC, и выполнил проверку попадания случайной точки в viewDidAppear. Я получаю только 1 вызов. Куда вы вызываете hitTest?

2. Также пробовал перехватывать события в UIWindow и удалять / разрывать цепочку ответчиков и т.д. Я все еще озадачен! 🙂