Функция распознавания жестов, прикрепленная к представлению контейнера, не блокирует событие касания кнопки в представлении контейнера, а блокирует событие касания кнопки панели инструментов

Таким образом, у меня есть контроллер представления, который имеет вид контейнера. В контейнерное представление встроен контроллер навигации, который также является родительским контроллером контроллера представления. Раскадровка выглядит так:

просмотр контроллера (mainViewController) -> контроллер навигации -> просмотр контроллера (contentViewController)

Вы можете увидеть скриншот раскадровки ниже.

Первая стрелка представляет собой переход от просмотра контейнера к контроллеру навигации. Вторая стрелка представляет собой отношение представляет contentViewController является корневым контроллером представления навигационного контроллера.

mainViewController а также contentViewController объекты одного и того же класса, названные testViewController, Это подкласс UIViewController. Его реализация проста. Всего три IBAction методы, больше ничего. Вот код реализации:

#import "TestViewController.h"

@implementation TestViewController

- (IBAction)buttonTapped:(id)sender {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil
                                                    message:@"button is tapped"
                                                   delegate:self
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
    [alert show];
}

- (IBAction)barButtonTapped:(id)sender
{
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil
                                                    message:@"bar button is tapped"
                                                   delegate:self
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
    [alert show];
}

- (IBAction)viewTapped:(id)sender {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil
                                                    message:@"view is tapped"
                                                   delegate:self
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles: nil];
    [alert show];
}        
@end

Я добавил Распознаватель жестов касания к представлению контейнера в mainViewController, Отправляет viewTapped:(id)sender сообщение для mainViewController когда вид контейнера коснулся. Внутри корневой вид contentViewControllerесть кнопка которая отправляет buttonTapped:(id)sender сообщение для contentViewController когда постучал. И есть панель кнопки на панели инструментов contentViewController который отправляет barButtonTapped:(id)sender сообщение для contentViewController когда постучал. Начальная сцена mainViewController, Когда приложение запущено, я обнаружил, что блокируются только события касания кнопки панели, событие касания обрабатывается кнопкой правильно. В документации Apple, регулирующей доставку штрихов к представлениям, говорится:

В простом случае, когда происходит касание, объект касания передается из объекта UIApplication в объект UIWindow. Затем окно сначала отправляет касания любым распознавателям жестов, прикрепленным к представлению, в котором произошли касания (или к суперпредставлениям этого представления), прежде чем оно передает касание самому объекту представления.

Я думал, что сенсорное событие не перейдет на кнопку. Это действительно смутило меня. Может кто-нибудь объяснить это поведение? Большое спасибо.


Снимок экрана раскадровки:раскадровка

1 ответ

Решение

Руководство по обработке событий для iOS: доставка событий: раздел "Сеть ответчика следует по определенному пути доставки" в разделе "Ответчик" описывает, как события касания передаются сначала в касание, которое было затронуто, затем вверх по всем его суперпредставлениям, а затем в окно, и, наконец, к самому приложению.

Упрощенное представление иерархии представления вашего проекта будет:

mainViewController's Root View
  | mainViewController's Container View (has Tap Gesture Recognizer)
  |   | UINavigationController's Root View
  |   |   | contentViewController's View
  |   |   |   | UIButton ("Button")
  |   |   | UINavigationController's Toolbar View
  |   |   |   | UIToolbarTextButton ("Item")

... поэтому, когда вы нажимаете кнопку или кнопку панели инструментов, они получают сенсорное событие перед представлением контейнера mainViewController.

Причина, по которой происходит событие кнопки, а кнопки панели инструментов, по-видимому, связана с разделом "Руководство по обработке событий для iOS": раздел "Взаимодействие с другими элементами управления пользовательского интерфейса" распознавателей жестов:

В iOS 6.0 и позже, управляющие действия по умолчанию предотвращают перекрывающееся поведение распознавателя жестов. Например, действие по умолчанию для кнопки - одно нажатие. Если у вас есть распознаватель жестов с одним касанием, прикрепленный к родительскому представлению кнопки, и пользователь нажимает кнопку, то метод действия кнопки получает событие касания вместо распознавателя жестов.

Это объясняет, почему UIButton в состоянии опередить распознаватель жестов касания, но он ничего не говорит о кнопке панели инструментов.

Если вы распечатаете иерархию видов, то увидите, что кнопка панели инструментов представлена ​​с помощью UIToolbarButton который является частным классом, который наследует непосредственно от UIControl, Исходя из наших наблюдений, мы предположили бы, что UIToolbarButton не вытесняет распознаватели жестов, как публика UIControl подклассы делают. Когда я залил его touchesCancelled:withEvent: Я обнаружил, что метод вызывается после срабатывания распознавателя жестов касания, что, по-видимому, и следовало ожидать на основании Руководства по обработке событий для iOS: раздел "Распознаватели жестов" в разделе "Распознаватели жестов" получает первую возможность распознать касание ", где они отмечают:

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

Есть несколько разных способов изменить это поведение, и выбор будет зависеть от вашей конечной цели. Если вы хотите разрешить касания на панели инструментов, вы можете проверить, если UITouch отправлено делегату распознавателя жестов gestureRecognizer:shouldReceiveTouch: был внутри рамки панели инструментов и вернуться NO если бы это было Блокировка касается UIButton в частности, вероятно, потребуется подкласс, но если вы хотите заблокировать все прикосновения к контроллерам дочернего представления mainViewController, вы можете добавить прозрачное представление к его представлению контейнера.

Другие вопросы по тегам