Как отменить нажатие кнопки, если UIGestureRecognizer срабатывает? - PullRequest
4 голосов
/ 31 мая 2011

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

В моем приложении для iPhone у меня есть несколько UIB-кнопок в качестве подпредставлений. В представлении также есть UITapGestureRecognizer, который прослушивает касания двумя пальцами.

Когда касание происходит двумя пальцами, я не хочу, чтобы кнопки реагировали на нажатие, даже если один из пальцев был внутри кнопки. Я думал, что это то, для чего "cancellsTouchesInView", но это не работает.

Теперь у меня вопрос: как заставить мои кнопки игнорировать нажатия при распознавании жеста?

Редактировать: Это мой распознаватель жестов.

UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTapped:)];
[doubleTap setNumberOfTouchesRequired:2];
[doubleTap setNumberOfTapsRequired:1];
[doubleTap setCancelsTouchesInView:YES];
[doubleTap setDelaysTouchesBegan:YES];
[doubleTap setDelaysTouchesEnded:YES];
[self.view addGestureRecognizer:doubleTap];
[doubleTap release];

Ответы [ 4 ]

2 голосов
/ 02 июня 2011

По словам разработчика Apple, это ошибка. Я подал отчет об ошибке в Apple. Большое спасибо за ваши советы, Дипак и gcamp!

Сообщение об ошибке:

Резюме: При добавлении двух UITapGestureRecognizer в представление, где один требует, чтобы другой отказал (requiredGestureRecognizerToFail :), свойство cancellsTouchesInView первого распознавателя жестов игнорируется.

Шаги для воспроизведения: 1. Создайте два UITapGestureRecognizer (r1 и r2) 2. Сконфигурируйте r1 так, чтобы требовалось два касания и одно касание, и чтобы откладывать касания Начать 3. Сконфигурируйте r2 так, чтобы требовалось два касания и два касания, и чтобы задерживать касания Начать 4. Сконфигурируйте r1 так, чтобы он требовал сбоя r2 [r1 requiredGestureRecognizerToFail: r2] 5. Добавьте r1 и r2 к представлению 6. Поместите кнопку UIB в вид 7. Коснитесь двумя пальцами вида, нужно нажать кнопку.

Ожидаемые результаты: r1 должен быть распознан, а нажатие кнопки должно быть отменено (по умолчанию для cancellsTouchesInView установлено значение YES для UITapGestureRecognizers).

Фактические результаты: r1 распознается, но также происходит событие кнопки touchedUpInside.

регрессия: cancelTouchesInView отлично работает для r1, как только вы удалите зависимость от r2 (шаг 4).

1 голос
/ 31 мая 2011

Используйте свойство delaysTouchesBegan.Установите значение YES.

Альтернатива

Отключите взаимодействие с пользователем на кнопках и подключите распознаватель касаний одним пальцем, как указано выше.В обработчике касания проверьте, попадает ли касание в пределы кнопки.Если он находится в пределах кнопки, выполните [theButton sendActionsForControlEvents:UIControlEventTouchUpInside];.Это будет вызывать событие касания по желанию, даже если взаимодействие с пользователем отключено.

1 голос
/ 31 мая 2011

В UIGestureRecognizer есть метод, который отвечает на ваш вопрос

- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer

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

Итак,

[singleTapRecognizer requireGestureRecognizerToFail:twoTapRecognizer];
0 голосов
/ 11 июля 2011

Марк, я столкнулся с той же ошибкой.Если вы опубликуете свой радар #, я буду дублировать его в bugreporter.

Я написал следующий обходной путь.Я создаю подкласс UITapGestureRecognizer, затем использую подкласс для отслеживания касаний, которые вызвали действие жеста.Если мы получим те же штрихи в штрихах, закругленных на виде, мы перенаправим на штрихи Отменено.'немедленная цель' необходима, потому что вызов touchesEnded в представлении происходит после прикосновения жеста, но до вызова действия жеста.

RPTapGestureRecognizer.h:

#import <UIKit/UIKit.h>
#import <UIKit/UIGestureRecognizerSubclass.h>

@interface RPTapGestureRecognizer : UITapGestureRecognizer

@property (nonatomic, retain) NSSet *lastTouches;

- (void)setImmediateTarget:(id)inTarget action:(SEL)inAction;

@end

RPTapGestureRecognizer.m:

#import "RPTapGestureRecognizer.h"

@interface RPTapGestureRecognizer ()
@property (nonatomic) SEL immediateAction;
@property (nonatomic, assign) id immediateTarget;
@end

@implementation RPTapGestureRecognizer

@synthesize lastTouches;
@synthesize immediateAction, immediateTarget;

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    self.lastTouches = nil;

    [super touchesBegan:touches withEvent:event];
}

- (void)setImmediateTarget:(id)inTarget action:(SEL)inAction
{
    self.immediateTarget = inTarget;
    self.immediateAction = inAction;
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    UIGestureRecognizerState startingState, finalState;

    startingState = self.state;
    [super touchesEnded:touches withEvent:event];
    finalState = self.state;

    if (startingState != finalState &&
        finalState == UIGestureRecognizerStateEnded) {
        /* Must copy; the underlying NSCFSet will be modified by the superclass, removing the touches */
        self.lastTouches = [[touches copy] autorelease];

        if (self.immediateAction)
            [self.immediateTarget performSelector:self.immediateAction
                                       withObject:self];
    }
}

- (void)dealloc
{
    self.lastTouches = nil;
    self.immediateTarget = nil;

    [super dealloc];
}

@end

По виду:

@synthesize lastTapGesture

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (self.lastTapGesture && [self.lastTapGesture.lastTouches isEqualToSet:touches]) {
        [self touchesCancelled:touches withEvent:event];
        self.lastTapGesture = nil;
        return;
    }
 /* touches ended implementation here */
}

 - (void)willHandleMouseTapGesture:(RPTapGestureRecognizer *)tapGesture
{
    /* Because one tap gesture is dependent upon another, the touchesEnded method is still going to be called, instead of touchesCanceled.
     * This is an Apple bug against all versions of iOS at least up to 5.0. It will be called in the run loop before tapGesture's action is called.
     *
     * See /7973431/kak-otmenit-nazhatie-knopki-esli-uigesturerecognizer-srabatyvaet#7973465 for details.
     */
    self.lastTapGesture = tapGesture;
}

- (void)configureGestures
{
    /* Two finger taps */
     RPTapGestureRecognizer *tapGestureOne;
     tapGestureOne = [[[RPTapGestureRecognizer alloc] initWithTarget:self 
                                                              action:@selector(handleMouseTapGesture:)] autorelease];
     tapGestureOne.numberOfTapsRequired = 1;
     tapGestureOne.numberOfTouchesRequired = 2;
     [tapGestureOne setImmediateTarget:self action:@selector(willHandleMouseTapGesture:)];
     [self addGestureRecognizer:tapGestureOne];
     [myGestures addObject:tapGestureOne];

     RPTapGestureRecognizer *tapGestureTwo;
     tapGestureTwo = [[[RPTapGestureRecognizer alloc] initWithTarget:self 
                                                              action:@selector(handleMouseTapGesture:)] autorelease];
     tapGestureTwo.numberOfTapsRequired = 2;
     tapGestureTwo.numberOfTouchesRequired = 2;
     [tapGestureTwo setImmediateTarget:self action:@selector(willHandleMouseTapGesture:)];
     [self addGestureRecognizer:tapGestureTwo];
}
...