为啥在我的 UIPanGestureRecognizer 上调用 UILongPressGestureRecognizer 方法?

Posted

技术标签:

【中文标题】为啥在我的 UIPanGestureRecognizer 上调用 UILongPressGestureRecognizer 方法?【英文标题】:Why does the UILongPressGestureRecognizer method get called on my UIPanGestureRecognizer?为什么在我的 UIPanGestureRecognizer 上调用 UILongPressGestureRecognizer 方法? 【发布时间】:2013-12-27 01:07:28 【问题描述】:

我有一个自定义 UITableViewCell,上面有一个平移手势识别器:

// CustomCell.m

- (id)initWithCoder:(NSCoder *)aDecoder 
    self = [super initWithCoder:aDecoder];
    if (self) 
        // Initialization code

        // Add a pan recognizer
        UIGestureRecognizer* recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
        recognizer.delegate = self;
        [self addGestureRecognizer:recognizer];
    
    return self;

我正在使用平移手势来检测用户是否以任何水平方式滑动单元格,然后单元格会根据单元格被滑动的程度执行一些不同的操作。细胞行为的这一方面正如我所不希望的那样工作。

但我刚刚发现,如果我按下单元格并且不向任何方向滑动,应用程序会由于调用 UILongPressGestureRecognizer 而崩溃,但我的应用程序中根本没有 UILongPressGestureRecognizer。 为什么在我的 UIPanGestureRecognizer 上调用 UILongPressGestureRecognizer 方法?

这是我的平移手势处理方法:

#pragma mark - Horizontal pan gesture methods

- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer 
    CGPoint translation = [gestureRecognizer translationInView:[self superview]];
    // Check for horizontal gesture
    if (fabsf(translation.x) > fabsf(translation.y)) 
        return YES;
    
    return NO;


#pragma mark - Handel pan gesture

- (void)handlePan:(UIPanGestureRecognizer *)recognizer 
    // 1
    if (recognizer.state == UIGestureRecognizerStateBegan) 
        // if the gesture has just started, record the current centre location
        originalCenter = self.center;
    

    // 2
    if (recognizer.state == UIGestureRecognizerStateChanged) 

        // translate the center
        CGPoint translation = [recognizer translationInView:self];
        self.center = CGPointMake(originalCenter.x + translation.x, originalCenter.y);

        // determine whether the item has been dragged far enough to initiate a delete / complete / edit
        editOnDragRelease = self.frame.origin.x < -self.frame.size.width / 4;
        deleteOnDragRelease = self.frame.origin.x < -self.frame.size.width / 1.5;
        markCompleteOnDragRelease = self.frame.origin.x > self.frame.size.width / 8;
    

    // 3
    if (recognizer.state == UIGestureRecognizerStateEnded) 
        // the frame this cell would have had before being dragged
        CGRect originalFrame = CGRectMake(0, self.frame.origin.y, self.bounds.size.width, self.bounds.size.height);

        if (!deleteOnDragRelease || !editOnDragRelease) 
            // if the item is not being deleted/edited, snap back to the original location
            [UIView animateWithDuration:0.2 animations:^
                self.frame = originalFrame;
            ];
        

        // Edit
        if (!deleteOnDragRelease && editOnDragRelease) 
            [self.delegate editItem:self.item];
        

        // Delete
        if (deleteOnDragRelease) 
            [self.delegate itemDeleted:self.item];
        

        // Toggle color
        if (markCompleteOnDragRelease) 
            [self.delegate toggleColorForItem:self.item];
        
    

我在崩溃日志中遇到的错误是当我选择一个单元格但不以任何方式滑动/平移它时:

-[UILongPressGestureRecognizer translationInView:]: unrecognized selector sent to instance 0x1093327c0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UILongPressGestureRecognizer translationInView:]: unrecognized selector sent to instance 0x1093327c0'

任何帮助将不胜感激!

【问题讨论】:

【参考方案1】:

崩溃可能发生在 gestureRecognizerShouldBegin: 委托方法中,该方法为此类实例作为委托的任何手势识别器调用。

这包括 ios 可能正在创建并将其自身分配给底层标准 UITableViewCell 的手势识别器。

iOS 必须将UILongPressGestureRecognizer 添加到单元格(供自己使用),并且为它调用委托方法以及您的UIPanGestureRecognizer

由于UILongPressGestureRecognizer 没有translationInView: 方法,这会导致“无法识别的选择器”崩溃。

至少,您可以检查gestureRecognizer 参数是否实际上是UIPanGestureRecognizer,如果不是,则什么也不做,只返回YES

还请注意,即使您已将gestureRecognizerShouldBegin: 方法的参数类型声明为UIPanGestureRecognizer *,这实际上并不能阻止任何UIGestureRecognizer * 调用它(即,将参数类型保留为通用UIGestureRecognizer *,因此含义和含义很清楚)。

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer 

    //if gestureRecognizer is not a UIPanGestureRecognizer, let it go...
    if (! [gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
    
        return YES;
    

    //Cast the gestureRecognizer as a UIPanGestureRecognizer so
    //compiler knows and checks what we're doing...
    UIPanGestureRecognizer *pgr = (UIPanGestureRecognizer *)gestureRecognizer;

    CGPoint translation = [pgr translationInView:[self superview]];
    // Check for horizontal gesture
    if (fabsf(translation.x) > fabsf(translation.y)) 
        return YES;
    
    return NO;

【讨论】:

非常感谢您的详细解答。这解决了崩溃并挽救了我的一天:)【参考方案2】:

UITableViewCell 有一个内置的UILongPressGestureRecognizer,它本身就是手势委托。所以当用户长按行并且tableView:shouldShowMenuForRowAtIndexPath:方法返回YES时,它可以显示编辑菜单(复制,粘贴等)。

这里的问题正如@Anna 在您的gestureRecognizerShouldBegin 方法中所说的那样。内置(父类)UITableViewCell'sUILongPressGestureRecognizer 共享您分配给平移手势的同一委托。

【讨论】:

以上是关于为啥在我的 UIPanGestureRecognizer 上调用 UILongPressGestureRecognizer 方法?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在我的 UIPanGestureRecognizer 上调用 UILongPressGestureRecognizer 方法?

为啥相同的代码在我的 BackGroundWorker 线程中比在我的 GUI 线程中慢得多?

为啥我的 PostBack 在我的 jQuery 点击事件之前发生?

为啥在我的 java 代码中没有调用通知

为啥我的 IOCompetionCallback 从未在我的 IO 完成端口上执行?

为啥 Symfony 2 在我的环境中响应非常缓慢?