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

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

контроллер представления (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. Затем окно сначала отправляет касания любым распознавателям жестов, прикрепленным к представлению, в котором произошли касания (или к суперпредставлениям этого представления), прежде чем передать касание самому объекту представления.

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


Скриншот раскадровки: раскадровка


person wcd    schedule 23.01.2014    source источник


Ответы (1)


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

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

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, вы можете добавить прозрачное представление поверх его представления контейнера.

person cszucko    schedule 27.09.2014