【问题标题】:How to cancel UIGestureRecognizer if subview's button pressed如果按下子视图的按钮,如何取消 UIGestureRecognizer
【发布时间】:2025-12-14 09:30:01
【问题描述】:

我正在努力从手势识别器中获得我想要的行为,特别是如果其他手势被触发则取消某些手势。

我有一个 scrollView 设置为分页和每个页面中的多个子视图。如果用户点击页面的右侧或左侧,我添加了一个触摸手势识别器以滚动到下一页或上一页。

    // Add a gesture recogniser turn pages on a single tap at the edge of a page
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureHandler:)];
    tapGesture.cancelsTouchesInView = NO;
    [self addGestureRecognizer:tapGesture];
    [tapGesture release];

还有我的手势处理程序:

- (void) tapGestureHandler:(UIGestureRecognizer *) gestureRecognizer {
    const CGFloat kTapMargin = 180;

    // Get the position of the point tapped in the window co-ordinate system
    CGPoint tapPoint = [gestureRecognizer locationInView:nil];

    // If the tap point is to the left of the page then go back a page
    if (tapPoint.x > (self.frame.size.width - kTapMargin)) [self scrollRectToVisible:pageViewRightFrame animated:YES];

    // If the tap point is to the right of the page then go forward a page
    else if (tapPoint.x < kTapMargin) [self scrollRectToVisible:pageViewLeftFrame animated:YES];
}

一切正常,除了我在页面上有一个带有按钮的子视图。如果用户触摸 subView 上的按钮,我希望能够忽略点击翻页,但我不知道该怎么做。

干杯

戴夫

【问题讨论】:

    标签: iphone cocoa-touch uibutton uigesturerecognizer


    【解决方案1】:

    最终对我来说效果最好的解决方案是使用 hitTest 来确定点击手势位置下方是否有任何按钮。如果有则忽略其余的手势代码。

    似乎运作良好。想知道我所做的是否有任何问题。

    - (void) tapGestureHandler:(UIGestureRecognizer *) gestureRecognizer {
        const CGFloat kTapMargin = 180;
    
        // Get the position of the point tapped in the window co-ordinate system
        CGPoint tapPoint = [gestureRecognizer locationInView:nil];
    
        // If there are no buttons beneath this tap then move to the next page if near the page edge
        UIView *viewAtBottomOfHeirachy = [self.window hitTest:tapPoint withEvent:nil];
        if (![viewAtBottomOfHeirachy isKindOfClass:[UIButton class]]) {
    
            // If the tap point is to the left of the page then go back a page
            if (tapPoint.x > (self.bounds.size.width - kTapMargin)) [self scrollRectToVisible:pageViewRightFrame animated:YES];
    
            // If the tap point is to the right of the page then go forward a page
            else if (tapPoint.x < kTapMargin) [self scrollRectToVisible:pageViewLeftFrame animated:YES];
        }   
    }
    

    【讨论】:

    • 这是我能想到的解决问题的最佳方式。太好了!
    • 这似乎是我正在寻找的答案,但是我在哪里实现这个 tapGestureHandler 呢?它是如何触发的?干杯。
    • @f0rz 我问题中的第一个代码块在我自己的类中设置了一个点击手势识别器,它是 UIView 的子类。本质上,您创建一个 UIGestureRecognizer 对象并使用 UIView 的 addGestureRecognizer: 方法来添加它。
    • @MagicBulletDave:我遇到了类似的问题,使用您的解决方案,如果层次结构底部有 UIButton,我取消了手势,但我想知道如何让我的 UIButton 获得按下事件它没有得到。
    • 嗨,Amogh,您需要确保您的手势识别器的 cancelsTouchesInView 属性设置为 NO。
    【解决方案2】:

    Apple documentation 显示答案:

    - (void)viewDidLoad {
          [super viewDidLoad];
          // Add the delegate to the tap gesture recognizer
          self.tapGestureRecognizer.delegate = self;
    }
    
    // Implement the UIGestureRecognizerDelegate method
    -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:    (UITouch *)touch {
          // Determine if the touch is inside the custom subview
         if ([touch view] == self.customSubview){
             // If it is, prevent all of the delegate's gesture recognizers
             // from receiving the touch
            return NO;
         }
         return YES;
    }
    

    当然在这种情况下 customSubview 将是页面上的子视图,其中包含按钮(甚至是按钮)

    【讨论】:

      【解决方案3】:

      斯威夫特 3

      设置UITapGestureRecognizer

      let tap = UITapGestureRecognizer(target: self, action: #selector(Class.didTap))
      tap.delegate = self
      

      UITapGestureRecognizer委托方法:

      func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
          return touch.view != buttonThatShouldCancelTapGesture
      }
      

      【讨论】:

        【解决方案4】:

        您可以继承 UIView 并覆盖 -touchesBegan 选择器,或者您可以使用子视图的 opaque 属性使它们对触摸“不可见”(如果 view.opaque = NO,视图将忽略触摸事件)。

        【讨论】:

        • 嗨鲁本,感谢您的建议。我不太确定他们会在这种情况下提供帮助。本质上,我想在 subView 中捕获按钮按下,然后在 superView 中取消触摸。据我了解,UIGestureRecognizer 将首先获得所有触摸,我不确定 UIButton 是使用手势识别器还是自制触摸来完成它的工作。
        • 好的,我通过在顶部视图中添加长按手势识别器解决了类似的情况:当用户长按页面时,它可以访问子视图(以及您想要的所有按钮) )。使用 UILongPressGestureRecognizer
        • 干杯鲁本,会试一试,让你知道我的进展情况。
        • 嗨鲁本,很抱歉我无法让它工作,感觉就像我在添加越来越多的代码。我想出了一个解决方案(请参阅我的答案)并且在我得到一些关于其有效性的反馈之前不会接受这个解决方案。感谢所有的帮助,戴夫。
        • 在我最后的回答中,我想提出一个可能不符合您要求的解决方案。我的观点是,您可以在上方视图中放置一个 UILongPressGestureRecognizer,当检测到该视图时,该视图会以动画方式消失并让用户触摸下方的所有按钮。那不是你真正需要的!很抱歉给您带来不便