Можно ли прикрепить UIGestureRecognizer к нескольким представлениям?

228
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapTapTap:)];
[self.view1 addGestureRecognizer:tapGesture];
[self.view2 addGestureRecognizer:tapGesture];
[tapGesture release];

В приведенном выше коде view2распознаются только нажатия . Если я закомментирую третью строку, то нажатия на view1распознаются. Если я прав, и вы можете использовать распознаватель жестов только один раз, я не уверен, является ли это ошибкой или просто нужна дополнительная документация.

куби
источник

Ответы:

334

А UIGestureRecognizerдолжен использоваться с одним видом. Я согласен, что документация пятнистая. Это UIGestureRecognizerимеет единственное viewсвойство, отдает его:

Посмотреть

Вид, к которому прикреплен распознаватель жестов. (Только для чтения)

@property (nonatomic, readonly) UIView * просмотр

Обсуждение Вы присоединяете (или добавляете) распознаватель жестов к объекту UIView, используя метод addGestureRecognizer:.

TomSwift
источник
11
Потому что добавление распознавателя жестов в представление происходит во время выполнения (по сравнению со временем компиляции).
TomSwift
1
Я получил это, но очень похоже на обнаружение того, что мы не использовали переменную, XCode может на основании кода сказать, что мы передали один и тот же распознаватель в несколько представлений и может предупредить кодировщика.
Золтан Маток
1
Предупреждение компилятора о нескольких представлениях, назначающих один и тот же UITapGestureRecognizer, является бессмысленным, потому что вы можете сделать это специально, например, если вы хотите переместить распознаватель жестов касания из одного представления в другое. Тем не менее, это глупое ограничение, что распознаватель жестов не может использоваться в нескольких представлениях.
Эрик ван дер Нойт
1
В iOS 9 теперь применяется одно представление для каждого распознавателя жестов. Я использовал метод конструктора интерфейса, описанный ниже, но теперь я получаю следующее сообщение, когда пытаюсь его использовать (некоторые детали приведены для краткости): ПРЕДУПРЕЖДЕНИЕ: Распознаватель жестов (< UITapGestureRecognizer: .....>) был настроен в раскадровке / xib для добавления в несколько представлений (-> <UIView:; frame = (0 44; 600 536); autoresize = RM + BM; жестRecognizers = < NSArray ...:>; layer = <CALayer: ... >>) одновременно, это никогда не разрешалось и теперь применяется принудительно. Начиная с iOS 9.0 он будет помещен в первый вид, в который он загружен.
Джордж Браун
Если вы добавляете в представление во второй раз, то представление было присоединено до того, как этот распознаватель автоматически отключился.UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didPressed:)]; [self.view1 addGestureRecognizer:tapRecognizer]; [self.view2 addGestureRecognizer:tapRecognizer]; Выходное представление view1 не имеет массива распознавателей жестов; view2 получил массив распознавателей жестов
kokos8998
48

Я обошел это, используя ниже.

for (UIButton *aButton in myButtons) {

            UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
            longPress.minimumPressDuration=1.0;
            [aButton addGestureRecognizer:longPress];
            [longPress release];

}

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

- (void)handleLongPress:(UILongPressGestureRecognizer*)gesture {
    if ( gesture.state == UIGestureRecognizerStateEnded ) {
        UIButton *whichButton=(UIButton *)[gesture view];
        selectedButton=(UIButton *)[gesture view];
    ....
}
kwalker
источник
1
Отличный ответ. Спасибо за тонну. Это мог быть принятый ответ, если бы вопрос был «Как вы присоединяете UIGestureRecognizer к нескольким представлениям?»
D_D
7
Это (или что-то очень близко к этому) не работает для меня. Я добавил несколько представлений в распознаватель жестов касания в Интерфейсном Разработчике и подключил распознаватель к действию. Действие вызывалось каждый раз, когда подключалось представление, но gest.view всегда был последним приложенным представлением.
Анейл Маллаварапу
Это действительно хороший ответ, а также очень полезный и согласен с @MicRO +1
Дилип
2
Aneil, это потому, что вы не создали новые экземпляры распознавателя жестов. Что происходит в цикле в этом ответе, так это то, что создаются новые экземпляры распознавателей жестов, каждый из которых имеет только одно присоединенное представление. Все они могут указывать на один и тот же обработчик, где вы затем проверяете вид, чтобы увидеть, какой из них был затронут.
Эрик ван дер Нойт
1
Может ли кто-то еще подтвердить, что это больше не работает в текущей версии Obj-C / Swift?
Макси Мус
18

Для Swift 3 на случай, если кому-то это понадобится: на основании ответа Bhavik Rathod выше.

 func setGestureRecognizer() -> UIPanGestureRecognizer {

        var panRecognizer = UIPanGestureRecognizer()

        panRecognizer = UIPanGestureRecognizer (target: self, action: #selector(pan(panGesture:)))
        panRecognizer.minimumNumberOfTouches = 1
        panRecognizer.maximumNumberOfTouches = 1
        return panRecognizer
    }

        ///set the recognize in multiple views
        view1.addGestureRecognizer(setGestureRecognizer())
        view2.addGestureRecognizer(setGestureRecognizer())
Джордж Асда
источник
3
это, в основном, создание нескольких жестов для двух представлений, по-прежнему одно и то же правило: каждый жест имеет только один вид, к которому нужно присоединиться
Абдельрхман
3
Нет, функция создает жест каждый раз, когда она
вызывается
2
название функции неверно. логическая функция здесь является функцией получения. так его и следует назвать: getGestureRecognizeпотому что это то, что делает эта функция
David Seek
Работай хорошо для меня! И код более чистый, чем создание нескольких переменных или помещение всего кода для создания внутри addGestureRecognizer
Codenator81
11

Мы можем сделать что-то вроде этого, это легко и просто

1) создайте функцию, как показано ниже в вашем контроллере (эта функция вернет GestureRecognizer)

-(UITapGestureRecognizer*)setRecognizer{
     UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(openProfile)];
     [gestureRecognizer setNumberOfTapsRequired:1];
     return gestureRecognizer;
}

2) теперь установите этот распознаватель в нескольких представлениях

[self.view1 addGestureRecognizer:[self setRecognizer]]; 
[self.view2 addGestureRecognizer:[self setRecognizer]];
Бхавик Ратод
источник
Это не работает для меня, когда я использую две метки вместо видов.
Михир Оза
3
@Mihir Oza, это не может работать на UILabels напрямую. Из-за ярлыков нет смысла для взаимодействия с пользователем. Если вы хотите добавить жесты для UILabels, добавьте одну строчку labelName..isUserInteractionEnabled = true в Swift. Затем добавьте жесты.
iOS
Слишком поздно, чувак, я уже исправил это. Но спасибо за предложение. Ваш комментарий будет полезен для пользователей стека. Оценил!
Михир Оза
1
Я думаю, что линия setNumberOfTapsRequired:1не нужна
Навид Аббас
9

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

Эта явная информация содержится в документации Apple:

Распознаватели жестов прикрепляются к представлению

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

Руководство по обработке событий для iOS - распознаватели жестов Apple Developer Library

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

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

Джозеф лорд
источник
4

Хорошо, если кто-то не хочет писать код для добавления представления жестов для нескольких кнопок, как, например , ответил kwalker выше, и хочет сделать это через Interface Builder, это может вам помочь.

1) Вы можете добавить распознаватель жестов длительного нажатия из библиотеки объектов так же, как добавляете другие объекты, такие как UIButtons и UILabels.

введите описание изображения здесь Первоначально я использовал только один

2) Установите ссылки на выходы UIButtonи отправьте действия с владельцем файла.

введите описание изображения здесь

Примечание. Если у вас несколько UIButton или любого другого объекта, вам потребуется отдельный распознаватель жестов для каждого из них. Для более подробной информации, пожалуйста, обратитесь к моему вопросу. Неправильный тег UIButton при распознавании жестов при длинном нажатии

Роган-патель
источник
С помощью IB очень легко связать более одного UIView с распознавателем гостя. Вопрос был о генерации кода.
AlexeyVMP
3

если у вас есть фиксированный вид, я предлагаю вам сделать что-то вроде этого

[self.view1 addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapTapTap:)]];
[self.view2 addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapTapTap:)]];

таким образом уменьшится несколько разных бесполезных переменных

Райналдио Лимарга
источник
3

Вы можете создать общее расширение для просмотра, чтобы легко добавлять распознаватели жестов. Это всего лишь пример, но это может выглядеть так

extension UIView {

    func setGestureRecognizer<Gesture: UIGestureRecognizer>(of type: Gesture.Type, target: Any, actionSelector: Selector, swipeDirection: UISwipeGestureRecognizer.Direction? = nil, numOfTaps: Int = 1) {
    let getRecognizer = type.init(target: target, action: actionSelector)

    switch getRecognizer {
    case let swipeGesture as UISwipeGestureRecognizer:
        guard let direction = swipeDirection else { return }
        swipeGesture.direction = direction
        self.addGestureRecognizer(swipeGesture)
    case let tapGesture as UITapGestureRecognizer:
        tapGesture.numberOfTapsRequired = numOfTaps
        self.addGestureRecognizer(tapGesture)
    default:
        self.addGestureRecognizer(getRecognizer)
    }
  }

}

Чтобы добавить распознавание в 2 касания в виде, вы просто позвоните:

let actionSelector = #selector(actionToExecute)
view.setGestureRecognizer(of: UITapGestureRecognizer.self, target: self, actionSelector: actionSelector, numOfTaps: 2)

Вы также можете легко добавить распознаватель пролистывания

view.setGestureRecognizer(of: UISwipeGestureRecognizer.self, target: self, actionSelector: actionSelector, swipeDirection: .down)

и так далее. Просто помните, что цель должна быть связана с селектором.

Мартин
источник
2

Переопределить класс на ' <UIScrollViewDelegate>'

И используйте этот метод в классе .m:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    return YES;
}

Этот метод поможет вам включить несколько пролистываний в одном представлении.

AnkitRox
источник
2

Как насчет перезаписи (воссоздания) вашего GestureRecognize каждый раз, когда вы добавляете распознаватель жестов, указывающий на одну и ту же функцию. В приведенном ниже случае это работает. Я использую IBOutletCollection

Свифт 2:

@IBOutlet var topicView: [UIView]!

override func viewDidLoad() {
        for view in self.topicView as [UIView] {
        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "viewClicked:"))
    }
}

func viewClicked(recognizer: UITapGestureRecognizer) {
    print("tap")
}
febaisi
источник
-6

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

- (void)viewDidLoad
{
    firstIV.tag = 501;
    secondIV.tag = 502;
    thirdIV.tag = 503;
    forthIV.tag = 504;

    [self addTapGesturetoImageView: firstIV];
    [self addTapGesturetoImageView: secondIV];
    [self addTapGesturetoImageView: thirdIV];
    [self addTapGesturetoImageView: forthIV];
}

-(void)addTapGesturetoImageView:(UIImageView*)iv
{
    iv.userInteractionEnabled = YES;
    UITapGestureRecognizer * textfielBGIVTapGasture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(textfielBGIVTapped:)];
    textfielBGIVTapGasture.numberOfTapsRequired = 1;
    [iv addGestureRecognizer:textfielBGIVTapGasture];
}

- (void)textfielBGIVTapped:(UITapGestureRecognizer *)recognizer {
    int tag = recognizer.view.tag-500;
    switch (tag) {
        case 1:
        {
            //firstIV tapped;
            break;
        }
        case 2:
        {
            //secondIV tapped;
            break;
        }
        case 3:
        {
            //thirdIV tapped;
            break;
        }
        case 4:
        {
            //forthIV tapped;
            break;
        }
        default: {
            break;
        }
    }
}
Дилип
источник
1
Вы создаете несколько распознавателей жестов; Мой первоначальный вопрос был о повторном использовании одного распознавателя жестов, чего вы не можете сделать.
Куби
1
Какой смысл добавлять 500все теги ваших просмотров, а затем вычитать 500? Почему бы просто не начать свои теги с 1(или даже 0) вместо 501?
ma11hew28
@ MattDiPasquale, не имеет значения, если вы хотите начать с 1его просто я скопировал этот код из моего приложения, откуда я его даю 501. Но да, не давайте 0bcoz, я где-то читал, что это всегда указывает на родительский взгляд, так что это создаст осложнения, поверьте мне, я столкнулся с этим.
Дилип
Ключевой текст в документации: «Представление устанавливает строгую ссылку на распознаватель жестов». это означает, что представление владеет жестом. Жест может иметь только одного владельца. Смотрите ссылку
Phantom59