UITapGestureRecognizer - одно касание и двойное касание


156

Я пытаюсь добавить 2 UITapGestureRecognizersк представлению, одно для одного касания и одно для событий двойного касания. Распознаватель с одним касанием работает как положено (сам по себе). Но я не могу заставить работать распознаватель двойного касания.

Пытались экспериментировать со свойствами , как: cancelsTouchesInView, delaysTouchesBeganи , delaysTouchesEndedно до сих пор не работает.

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

Документация, кажется, предполагает, что 3 свойства, упомянутые выше, могут быть использованы для этой цели. Но я просто не уверен, какие значения должны быть установлены и на какие распознаватели (одинарные, двойные или оба). Надеюсь, кто-то знакомый с этим может помочь.

Ниже приведен последний обновленный блок кода.

// ****** gesture recognizers ******

- (void)addSingleAndDoubleTapGestureRecognizersToView:(UIView *)view
{
    // single tap    
    UITapGestureRecognizer *singleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget: tableViewController action: @selector(handleSingleTapOnView:)];                                 
    [singleTapRecognizer setNumberOfTouchesRequired:1];
    [view addGestureRecognizer: singleTapRecognizer];

    // double tap 
    UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget: tableViewController action: @selector (handleDoubleTapOnView:)];        
    [doubleTapRecognizer setNumberOfTouchesRequired:2];         
    [singleTapRecognizer requireGestureRecognizerToFail: doubleTapRecognizer];
    [view addGestureRecognizer: doubleTapRecognizer];         
}

- (void)handleSingleTapOnView:(id)sender
{

}

- (void)handleDoubleTapOnView:(id)sender
{

}

30
Случайный комментарий: подчеркивание в именах переменных заставит большинство программистов Objective-C съеживаться. Что еще хуже, ваши имена переменных не являются описательными. Почему бы просто не использовать singleTapRecognizer и doubleTapRecognizer (или что-то подобное)? Это занимает больше секунды, чтобы набрать, но делает ваш код бесконечно более читабельным.
UIAdam

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

В случае, если у кого-то еще есть подобная проблема. Я изо всех сил пытался заставить работать двойные касания и распознаватели одного касания в быстром режиме (одно касание работало нормально, но двойного касания не было). Я перетащил распознаватели на сцену, используя раскадровку. У меня были обработчики и оператор: singleTapRecognizer.requireGestureRecognizerToFail (doubleTabRecognizer) внутри моего viewDidLoad, но по-прежнему нет двойного нажатия. Моя недостающая часть головоломки заключалась в том, чтобы перетащить значок распознавателя двойного касания (из верхнего ряда сцены) на ту часть вида, где должно было быть распознано двойное касание.
Паулюс

1
@UIAdam, использующий подчеркивание для префикса частного имени ивара в objc, довольно распространен, хотя нет? (из-за быстрого фона)
Чарльтон Проватас

Ответы:


412
UITapGestureRecognizer *singleTap = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doSingleTap)] autorelease];
singleTap.numberOfTapsRequired = 1; 
[self.view addGestureRecognizer:singleTap];

UITapGestureRecognizer *doubleTap = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doDoubleTap)] autorelease];
doubleTap.numberOfTapsRequired = 2; 
[self.view addGestureRecognizer:doubleTap];

[singleTap requireGestureRecognizerToFail:doubleTap];

Примечание: если вы используете numberOfTouchesRequired это должно быть .numberOfTouchesRequired = 1;

Для Свифта

let singleTapGesture = UITapGestureRecognizer(target: self, action: #selector(didPressPartButton))
singleTapGesture.numberOfTapsRequired = 1
view.addGestureRecognizer(singleTapGesture)

let doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(didDoubleTap))
doubleTapGesture.numberOfTapsRequired = 2
view.addGestureRecognizer(doubleTapGesture)

singleTapGesture.require(toFail: doubleTapGesture)

Спасибо за ответ, попробовал ваш код, но он все еще не работает. Там должно быть что-то, что я пропустил. У вас есть еще подсказка?
Стэнли

Могу ли я иметь ваш файл реализации для этого?
Анил Котари

@Stanley: ответ Anils должен работать, конечно же, вы также должны предоставить обработчики (doSingleTap, doDoubleTap)
Rok Jarc

Одно касание обновляет UITableView, который работает как положено. Двойное касание, на данный момент, просто выводит лог-оператор с помощью NSLog. Я подозреваю, что статус первого респондента переходит из UIView в другое место после первого касания, так как я ничего не сделал явно с цепочкой ответов.
Стэнли

6
Решение работает для меня. Селектор с одним касанием срабатывает после некоторой задержки.
Владимир Обризан

47

Решение Swift 3:

let singleTap = UITapGestureRecognizer(target: self, action:#selector(self.singleTapAction(_:)))
singleTap.numberOfTapsRequired = 1
view.addGestureRecognizer(singleTap)

let doubleTap = UITapGestureRecognizer(target: self, action:#selector(self.doubleTapAction(_:)))
doubleTap.numberOfTapsRequired = 2
view.addGestureRecognizer(doubleTap)

singleTap.require(toFail: doubleTap)

В строке кода singleTap.require (toFail: doubleTap) мы вынуждаем однократное нажатие и ждем, чтобы событие касания не было двойным касанием.


31

Вам необходимо использовать requireGestureRecognizerToFail: метод. Что-то вроде этого:

[singleTapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];


Попытался использовать вызов метода «Fail» для обоих распознавателей только сейчас снова. Но все равно не работает. Если вы уже работали с двойным нажатием, поделитесь со мной своим опытом.
Стэнли

1
Я попробовал этот точный код (такой же, как тот, что Анил опубликовал более подробно), и он работает для меня.
UIAdam

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

Ваш код все еще мешает с задержками касаний и еще много чего. Сделайте так, чтобы он точно следовал коду Анила. Используйте requireGestureRecognizerToFail только один раз, как у нас.
UIAdam

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

15

// ---- во-первых, вы должны выделить двойной и один жест касания ------- //

UITapGestureRecognizer* doubleTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : @selector (handleDoubleTap:)];

UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : @selector (handleSingleTap:)];

[singleTap requireGestureRecognizerToFail : doubleTap];
[doubleTap setDelaysTouchesBegan : YES];
[singleTap setDelaysTouchesBegan : YES];

// ----------------------- количество нажатий ---------------- //

[doubleTap setNumberOfTapsRequired : 2];
[singleTap setNumberOfTapsRequired : 1];

// ------- добавление двойного касания и однократного касания к виду или кнопке -------- //

[self.view addGestureRecognizer : doubleTap];
[self.view addGestureRecognizer : singleTap];

10

Не уверен, что это именно то, что вы ищете, но я сделал одно / двойное нажатие без распознавания жестов. Я использую его в UITableView, поэтому я использовал этот код в методе didSelectRowAtIndexPath

    tapCount++;
    switch (tapCount)
    {
        case 1: //single tap
            [self performSelector:@selector(singleTap:) withObject: indexPath afterDelay: 0.2];
            break;
        case 2: //double tap
            [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(singleTap:) object:indexPath];
            [self performSelector:@selector(doubleTap:) withObject: indexPath];
            break;
        default:
            break;
    }
    if (tapCount>2) tapCount=0;

Методы singleTap и doubleTap просто недействительны с NSIndexPath в качестве параметра:

- (void)singleTap:(NSIndexPath *)indexPath {
  //do your stuff for a single tap
}

- (void)doubleTap:(NSIndexPath *)indexPath {
  //do your stuff for a double tap
}

Надеюсь, поможет


Спасибо за ответ, @Novarg. У вас были проблемы с распознавателями DoubleTap?
Стэнли

@ Stanley Нет, SingleTap ждет 0,2 секунды, прежде чем выстрелить. И если между тем был активирован второй тап, то метод singleTap отменяется.
Novarg

Это выглядит очень многообещающе, спасибо за вашу помощь. У меня сегодня снова не хватает времени. Завтра обязательно попробую :)
Стэнли

Креативный ответ;)
Майкл

очень хорошее решение. Мне просто нужно было добавить tapCount = 0 на каждый обработчик (singleTap, doubleTap), чтобы быть уверенным, что следующие касания также распознаются.
Сирио

3

Решение для Swift 2:

let singleTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleSingleTap))
singleTapGesture.numberOfTapsRequired = 1 // Optional for single tap
view.addGestureRecognizer(singleTapGesture)

let doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap))
doubleTapGesture.numberOfTapsRequired = 2
view.addGestureRecognizer(doubleTapGesture)

singleTapGesture.requireGestureRecognizerToFail(doubleTapGesture)

1

Я реализовал методы UIGestureRecognizerDelegate для обнаружения как singleTap, так и doubleTap.

Просто сделай это.

 UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleDoubleTapGesture:)];
    [doubleTap setDelegate:self];
    doubleTap.numberOfTapsRequired = 2;
    [self.headerView addGestureRecognizer:doubleTap];

    UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleSingleTapGesture:)];
    singleTap.numberOfTapsRequired = 1;
    [singleTap setDelegate:self];
    [doubleTap setDelaysTouchesBegan:YES];
    [singleTap setDelaysTouchesBegan:YES];
    [singleTap requireGestureRecognizerToFail:doubleTap];
    [self.headerView addGestureRecognizer:singleTap];

Затем реализуйте эти методы делегата.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
    return  YES;
}

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

0

Некоторые виды имеют встроенные распознаватели двойных касаний ( MKMapViewнапример). Чтобы обойти это, вам нужно реализовать UIGestureRecognizerDelegateметод shouldRecognizeSimultaneouslyWithGestureRecognizerи вернуть YES:

Сначала реализуйте свои двойные и одиночные распознаватели:

// setup gesture recognizers
UITapGestureRecognizer* singleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                                                action:@selector(mapViewTapped:)];
singleTapRecognizer.delegate = self;
singleTapRecognizer.numberOfTapsRequired = 1;


UITapGestureRecognizer* doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                                                      action:@selector(mapViewDoubleTapped:)];
doubleTapRecognizer.delegate = self;    // this allows
doubleTapRecognizer.numberOfTapsRequired = 2;
[singleTapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];

А затем реализовать:

#pragma mark UIGestureRecognizerDelegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer
*)otherGestureRecognizer {  return YES; }

-1

В отношении комментария @ Стэнли -

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

Но вы должны установить «Отмена касаний в представлении» на «НЕТ» на ваших распознавателях жестов одним касанием, иначе оно никогда не получит события касания.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.