如何缩短“delayTouchesBegan”延迟“touchesBegan”的时间?

19

在我的一个视图控制器中,我有几个包含 UITapGestureRecognizer 的视图,还有一个实现了 touchesBegan 方法。我需要优先处理点击事件而非 touchesBegan ,因此将手势识别器(gesture recognizers)的 delaysTouchesBegan 属性设置为 YES。这样做是正确的,但问题在于手势识别器会让 touchesBegan 延迟太久。根据文档

当该属性值为 YES 时,窗口会暂停向视图传递 UITouchPhaseBegan 阶段的触摸对象。如果手势识别器随后识别其手势,则会丢弃这些触摸对象。如果手势识别器未能识别其手势,则窗口会通过 touchesBegan:withEvent: 消息将这些对象传递给视图(并可能通过 touchesMoved:withEvent: 消息向视图报告触摸的当前位置)。

问题基本上是当手势识别器不能识别手势并将这些对象传递给 touchesBegan 时,该操作需要太长时间。有没有任何方法可以加快它,还是说处理手势以确定它是否为点击的过程非常繁琐,不可能缩短时间?


编辑:

以下是我用于设置手势识别器的代码:

UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
tapRecognizer.cancelsTouchesInView = NO;
tapRecognizer.delaysTouchesBegan = YES;
tapRecognizer.delegate = self;
tapRecognizer.numberOfTapsRequired = 1;
tapRecognizer.numberOfTouchesRequired = 1;
[self.someView addGestureRecognizer:tapRecognizer];

请更新您的答案,谢谢。 - Hussain Shabbir
虽然这种方法可能不适用于您的项目,但拦截一个视图中的触摸事件以防止其传递到另一个视图的简单方法是实现子视图的 hitTest 方法(即 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event)。 - EricLeaf
@qegal 你觉得我的回答没什么用处吗? - Segev
@Sha - 抱歉,我一直很忙没来得及测试。不过看起来这是目前最好的答案,所以我会给你悬赏。 - pasawaya
3个回答

28

我会通过将 UITapGestureRecognizer 更改为 UILongPressGestureRecognizer 来解决它。这两个手势的标题有点误导,但是使用 UILongPressGestureRecognizer 您可以设置 minimumPressDuration:

手指在视图上必须按下的最短时间才能识别手势。

UILongPressGestureRecognizer *tapRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
tapRecognizer.delegate = self;
tapRecognizer.minimumPressDuration = //Up to you;
[self.someView addGestureRecognizer:tapRecognizer];

2
有没有任何方法可以加快它,或者只是处理手势以确定它是否为轻拍的过程很复杂,不可能缩短?延迟似乎取决于处理手势所需的时间,因此无法调整它(您可以记录-touchesBegan:withEvent:以查看确切的调用时间)。例如,如果您使用UITapGestureRecognizer触摸UIView并且不移动手指,则轻拍识别器仍然认为您有机会在相同位置抬起手指,这将被识别为轻拍。因此,它将继续等待,直到您滑动手指或将其抬起。另一方面,如果立即平移,则UIView几乎没有延迟就会发送-touchesBegan:withEvent:。
您可以尝试的解决方法是:
1.改用手势识别器的cancelTouchesInView属性。如果将其设置为YES,则UIView将立即处理触摸,然后如果手势识别器最终将触摸识别为手势,则视图将发送-touchesCancelled:withEvent:,在其中可以回滚所做的任何更改。 2.在UIView中使用cancelTouchesInView和NSTimer。在UIView的-touchesBegan:withEvent:中启动NSTimer,当其触发时,在视图上执行操作。如果视图在计时器触发之前发送了-touchesCancelled:withEvent:,则取消计时器。 3.子类化UIGestureRecognizer并实现自己的轻拍识别器:)
我已经制作了选项1和2的示例。该示例具有可以在屏幕上拖动的视图,该视图具有更改视图颜色的轻拍识别器。如果您轻拍视图,但在释放之前稍微拖动它,则会看到“回滚”将视图捕捉到触摸前的位置。如果将JCPanningView的delayResponseToTouch设置为YES,则在延迟期内不会看到任何拖动。
这可能适用于您,具体取决于您的UIView如何处理其触摸。
以下是视图控制器的接口(JCGestureViewController.h):
#import <UIKit/UIKit.h>

@interface JCGestureViewController : UIViewController
@end

@interface JCPanningView : UIView
@property (nonatomic) BOOL delayResponseToTouch;
- (void)changeColor;
@end

实现(JCGestureViewController.m):

#import "JCGestureViewController.h"

@interface JCGestureViewController ()
@property (strong, nonatomic) JCPanningView *panningView;
@end

@implementation JCGestureViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    _panningView = [[JCPanningView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 200.0f, 200.0f)];
    [self.view addSubview:_panningView];

    UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                                                    action:@selector(tappedPanningView)];
    tapRecognizer.cancelsTouchesInView = YES;
    [_panningView addGestureRecognizer:tapRecognizer];

    // set this to YES to have a timer delay the view's response to touches
    _panningView.delayResponseToTouch = NO;
}

- (void)tappedPanningView
{
    [self.panningView changeColor];
}

@end

@interface JCPanningView ()

@property (nonatomic) CGPoint touchBeganLocation;
@property (nonatomic) CGPoint centerWhenTouchBegan;
@property (nonatomic) BOOL respondToTouches;
@property (nonatomic) NSTimer *timer;

@end

@implementation JCPanningView

- (void)dealloc
{
    if (_timer) {
        [_timer invalidate];
        _timer = nil;
    }
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [self randomColor];
    }
    return self;
}

- (void)changeColor
{
    self.backgroundColor = [self randomColor];
}

- (CGFloat)randomRGBValue
{
    return (arc4random() % 255) / 255.0f;
}

- (UIColor *)randomColor
{
    return [UIColor colorWithRed:[self randomRGBValue] green:[self randomRGBValue] blue:[self randomRGBValue] alpha:1.0f];
}

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

    UITouch *touch = [touches anyObject];
    self.touchBeganLocation = [touch locationInView:self.superview];
    self.centerWhenTouchBegan = self.center;

    if (self.delayResponseToTouch) {
        if (self.timer) {
            [self.timer invalidate];
        }
        self.timer = [NSTimer scheduledTimerWithTimeInterval:0.2
                                                      target:self
                                                    selector:@selector(startRespondingToTouches)
                                                    userInfo:nil
                                                     repeats:NO];
    }
}

- (void)startRespondingToTouches
{
    self.respondToTouches = YES;
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    self.center = self.centerWhenTouchBegan;

    if (self.timer) {
        [self.timer invalidate];
        self.timer = nil;
    }

    if (self.delayResponseToTouch) {
        self.respondToTouches = NO;
    }
}

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

    if (self.delayResponseToTouch && !self.respondToTouches) {
        return;
    }

    UITouch *touch = [touches anyObject];
    CGPoint newLocation = [touch locationInView:self.superview];
    CGPoint delta = CGPointMake(newLocation.x - self.touchBeganLocation.x, newLocation.y - self.touchBeganLocation.y);
    self.center = CGPointMake(self.centerWhenTouchBegan.x + delta.x, self.centerWhenTouchBegan.y + delta.y);
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (self.delayResponseToTouch) {
        self.respondToTouches = NO;
    }
}

@end

-1

因此,当您点击需要延迟触摸的视图时,您可以使用所需的时间启动计时器,一旦时间完成,就可以调用所需的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接