Как распознать oneTap / doubleTap в данный момент? - PullRequest
10 голосов
/ 08 ноября 2011

Я знаю, как фильтровать oneTap / doubleTap с помощью Apple API. код следующий.

UITapGestureRecognizer *doubleTapGestureRecognizer = [[UITapGestureRecognizer alloc]
                        initWithTarget:self action:@selector(handleDoubleTap:)];
doubleTapGestureRecognizer.numberOfTapsRequired = 2;


[self addGestureRecognizer:doubleTapGestureRecognizer];


UITapGestureRecognizer *singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];
singleTapGestureRecognizer.numberOfTapsRequired = 1;

**[singleTapGestureRecognizer requireGestureRecognizerToFail: doubleTapGestureRecognizer];**

[self addGestureRecognizer:singleTapGestureRecognizer];

но oneTap / doubleTap checkDelayTime чувствует себя так долго (около 0,5 с?). Вообще приложение пользователи реакции очень быстро. Хотя 0,5 секунды это обычно короткое время. но в среде мобильных устройств это долго, потому что реагировать пользователей очень важно.

Кстати, YouTubeApp enter image description here имеет очень совершенный алгоритм фильтрации на данный момент oneTap / doubleTap. oneTap-doubleTap checkDelay - VeryVeryShort Perfectly Optimization.

oneTap (показать / скрытая панель управления)

doubleTap (full / default videoScreenSize)

Как реализовать как YoutubeApp? о фильтрации oneTap-doubleTap Не используется селектор requireGestureRecognizerToFail. о очень короткой задержке различения oneTap-doubleTap.

Я думаю, что YoutubeApp не использует селектор requireGestureRecognizer.

Ответы [ 6 ]

21 голосов
/ 01 мая 2014

Самый простой способ сделать это - создать подкласс UITapGestureRecognizer , а не общий UIGestureRecognizer.

Как это:

#import <UIKit/UIGestureRecognizerSubclass.h>

#define UISHORT_TAP_MAX_DELAY 0.2
@interface UIShortTapGestureRecognizer : UITapGestureRecognizer

@end

Ипросто реализовать:

@implementation UIShortTapGestureRecognizer

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(UISHORT_TAP_MAX_DELAY * NSEC_PER_SEC)), dispatch_get_main_queue(), ^
    {
        // Enough time has passed and the gesture was not recognized -> It has failed.
        if  (self.state != UIGestureRecognizerStateRecognized)
        {
            self.state = UIGestureRecognizerStateFailed;
        }
    });
}
@end
15 голосов
/ 08 ноября 2011

Это проще всего сделать без распознавателей жестов.Тогда вы можете контролировать задержку.Код ниже представляет собой вариант оригинальной документации Apple, которую я использую в одном из своих проектов.У меня есть сообщение в блоге, которое также говорит об этом .

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
 UITouch *touch = [touches anyObject];
if (touch.tapCount == 2) {
//This will cancel the singleTap action
[NSObject cancelPreviousPerformRequestsWithTarget:self];
}

}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
 UITouch *touch = [touches anyObject];
if (touch.tapCount == 1) {
  //if they tapped within the coin then place the single tap action to fire after a delay of 0.3
  if (CGRectContainsPoint(coin.frame,[touch locationInView:self.view])){
    //this is the single tap action being set on a delay
  [self performSelector:@selector(onFlip) withObject:nil afterDelay:0.3];
  }else{
   //I change the background image here
  }
 } else if (touch.tapCount == 2) {
  //this is the double tap action
  [theCoin changeCoin:coin];
 }
}
13 голосов
/ 08 ноября 2011

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

[singleTapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];

тогда весь код становится:

UITapGestureRecognizer *doubleTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(beginComicTransitions:)] autorelease];    
doubleTapRecognizer.numberOfTapsRequired = 2;
doubleTapRecognizer.numberOfTouchesRequired = 1;
doubleTapRecognizer.delegate = self;   

UITapGestureRecognizer *singleTapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(bringMenu:)] autorelease];    
singleTapRecognizer.numberOfTapsRequired = 1;
singleTapRecognizer.numberOfTouchesRequired = 1;
singleTapRecognizer.delegate = self;

[singleTapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];

вот requireGestureRecognizerToFail означает:

  • если не распознано двойное касание, то распознается одно касание
  • если распознано двойное касание, не распознает одно касание

код быстрой версии:

    let doubleTap = UITapGestureRecognizer(target: self, action: "doubleTapped:")
    doubleTap.numberOfTapsRequired = 2
    doubleTap.numberOfTouchesRequired = 1
    self.scrollView.addGestureRecognizer(doubleTap)

    let singleTap = UITapGestureRecognizer(target: self, action: "singleTap:")
    singleTap.numberOfTapsRequired = 1
    singleTap.numberOfTouchesRequired = 1
    self.scrollView.addGestureRecognizer(singleTap)

    singleTap.requireGestureRecognizerToFail(doubleTap)
1 голос
/ 21 сентября 2013

Вот простые пользовательские распознаватели жестов для двойных нажатий, где вы можете указать максимально допустимое время между нажатиями. Это основано на ответе @Walters.

PbDoubleTapGestureRecognizer.h:

@interface PbDoubleTapGestureRecognizer : UIGestureRecognizer

@property (nonatomic) NSTimeInterval maximumDoubleTapDuration;

@end

PbDoubleTapGestureRecognizer.m:

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

@interface PbDoubleTapGestureRecognizer ()
@property (nonatomic) int tapCount;
@property (nonatomic) NSTimeInterval startTimestamp;
@end

@implementation PbDoubleTapGestureRecognizer

- (id)initWithTarget:(id)target action:(SEL)action {
    self = [super initWithTarget:target action:action];
    if (self) {
        _maximumDoubleTapDuration = 0.3f; // assign default value
    }
    return self;
}

-(void)dealloc {
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
}

- (void)reset {
    [super reset];

    [NSObject cancelPreviousPerformRequestsWithTarget:self];

    self.tapCount = 0;
    self.startTimestamp = 0.f;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesBegan:touches withEvent:event];

    if (touches.count != 1 ) {
        self.state = UIGestureRecognizerStateFailed;
    } else {
        if (self.tapCount == 0) {
            self.startTimestamp = event.timestamp;
            [self performSelector:@selector(timeoutMethod) withObject:self afterDelay:self.maximumDoubleTapDuration];
        }
        self.tapCount++;
    }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesMoved:touches withEvent:event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];

    if (self.tapCount > 2) {
        self.state = UIGestureRecognizerStateFailed;
    } else if (self.tapCount == 2 && event.timestamp < self.startTimestamp + self.maximumDoubleTapDuration) {
        [NSObject cancelPreviousPerformRequestsWithTarget:self];
        NSLog(@"Recognized in %f", event.timestamp - self.startTimestamp);
        self.state = UIGestureRecognizerStateRecognized;
    }
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesCancelled:touches withEvent:event];
    self.state = UIGestureRecognizerStateFailed;
}

- (void)timeoutMethod {
    self.state = UIGestureRecognizerStateFailed;
}

@end

Вы можете использовать это так:

PbDoubleTapGestureRecognizer *doubleTapGr = [[PbDoubleTapGestureRecognizer alloc]initWithTarget:self action:@selector(_doubleTapAction)];
doubleTapGr.maximumDoubleTapDuration = 0.4;
[yourView addGestureRecognizer:doubleTapGr];

Вы можете комбинировать это с requireGestureRecognizerToFail:, чтобы получить запрашиваемое поведение.

0 голосов
/ 14 июля 2017

Swift 3.1 версия ответа eladleb's .

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesBegan(touches, with: event)

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
            if self?.state != .recognized {
                self?.state = .failed
            }
        }
    }
0 голосов
/ 18 августа 2014
@interface NaMeClass ()

@property (nonatomic, strong) UITapGestureRecognizer * singleTap;
@property (nonatomic, strong) NSTimer *timer;

@end

// ... код ...

//viewDidLoad
self.singleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapIcon:)];
self.singleTap.numberOfTapsRequired = 1;
self.singleTap.cancelsTouchesInView = YES;
self.singleTap.delaysTouchesBegan = YES;
[self addGestureRecognizer:self.singleTap];

//.....code

-(void)tapIcon:(UITapGestureRecognizer *)tapGesture
{
    if (tapGesture.state == UIGestureRecognizerStateEnded){
        if (!self.timer) {
            self.timer = [NSTimer scheduledTimerWithTimeInterval:0.2
                         target:self selector:@selector(singleTap) userInfo:nil repeats:NO];
        }else{
            [self doubleTap];
     }
}

}

-(void)singleTap{
    [self.timer invalidate];
    self.timer = nil;
    NSLog(@"1111111111111");
}

-(void)doubleTap{
    [self.timer invalidate];
    self.timer = nil;
    NSLog(@"22222222222");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...