UIGestureRecognizer阻止子视图以处理触摸事件


82

我正在尝试找出正确的方法。我试图描述这种情况: 在此处输入图片说明

我要添加一个UITableView作为的子视图UIView。在UIView一个分接开关和响应pinchGestureRecognizer,但这样做的情况下,实现代码如下停止反应,这两个手势(它仍然反应刷卡)。

我已经将其与以下代码一起使用,但这显然不是一个不错的解决方案,并且我相信有更好的方法。这放在UIView(超级视图)中:

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    if([super hitTest:point withEvent:event] == self) {
        for (id gesture in self.gestureRecognizers) {
            [gesture setEnabled:YES];
        }
        return self;
    }
    for (id gesture in self.gestureRecognizers) {
        [gesture setEnabled:NO];
    }
    return [self.subviews lastObject];
}

Answers:


182

我有一个非常类似的问题,并且在这个SO问题中找到了解决方案。总而言之,将自己设置为您的代表UIGestureRecognizer,然后在允许识别器处理触摸之前检查目标视图。相关的委托方法是:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
       shouldReceiveTouch:(UITouch *)touch

3
我最喜欢此解决方案,因为它不涉及混乱的触摸hitTest:withEvent:pointInside:withEvent:
DarkDust 2011年

6
干净的解决方案,您可以进行测试,例如 return !(touch.view == givenView);,是否只想排除给定视图,或者return !(touch.view.tag == kTagNumReservedForExcludingViews);何时要停止识别器处理一大堆不同的子视图上的触摸。
2012年

6
我会用进行命中测试- (BOOL)isDescendantOfView:(UIView *)view。在- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event子类化UIGestureRecognizer时,这也可以正常工作。
Christoph

1
@Justin,如果我们的手势识别器属于uiscrollview并且我们无法委派手势识别器怎么办?
格克汗阿克尔巴里斯

108

将触摸事件阻止到子视图是默认行为。您可以更改以下行为:

UITapGestureRecognizer *r = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(agentPickerTapped:)];
r.cancelsTouchesInView = NO;
[agentPicker addGestureRecognizer:r];

5
这会将触摸事件发送到子视图,但也将发送到手势识别器。因此,这将阻止子视图的阻塞,但手势识别器仍将在子视图上被识别。
乔纳森。

@乔纳森 是的,我同意你的看法。我在手势处理程序方法中所做的工作是检查手势位置是否在相关子视图内发生,在这种情况下,我无需执行其余代码。另外,我选择此替代方法的原因之一是UITapGestureRecognizer不声明translationInView方法。因此,实现上述UIGestureRecognizerDelegate方法只会导致崩溃并出错... unrecognized selector sent to blah。要进行检查,请使用类似:的内容CGRectContainsPoint(subview.bounds, [recognizer locationInView:subview])
MkVal 2014年

根据OP的问题,应选择此答案作为主要答案。
farzadshbfn

5

我正在显示一个具有自己的表视图的下拉子视图。结果,touch.view有时会返回的类UITableViewCell。我必须逐步检查超类,以确保它是我认为是的子类:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    UIView *view = touch.view;
    while (view.class != UIView.class) {
        // Check if superclass is of type dropdown
        if (view.class == dropDown.class) { // dropDown is an ivar; replace with your own
            NSLog(@"Is of type dropdown; returning NO");
            return NO;
        } else {
            view = view.superview;
        }
    }

    return YES;
}

while循环说明了这一点
joslinm '16

5

建立在@Pin Shih Wang的答案上。我们将忽略除包含轻击手势识别器的视图上的所有轻击。按照我们的设置,所有轻按都会转发到视图层次结构tapGestureRecognizer.cancelsTouchesInView = false。这是Swift3 / 4中的代码:

func ensureBackgroundTapDismissesKeyboard() {
    let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
    tapGestureRecognizer.cancelsTouchesInView = false
    self.view.addGestureRecognizer(tapGestureRecognizer)
}

@objc func handleTap(recognizer: UIGestureRecognizer) {
    let location = recognizer.location(in: self.view)
    let hitTestView = self.view.hitTest(location, with: UIEvent())
    if hitTestView?.gestureRecognizers?.contains(recognizer) == .some(true) {
        // I dismiss the keyboard on a tap on the scroll view
        // REPLACE with own logic
        self.view.endEditing(true)
    }
}

如何将手势识别器与UISwipeActionStandardButton?我尝试过.some(UISwipeActionStandardButton)
Jalil

4

一种可能性是对手势识别器进行子分类(如果尚未分类)并进行覆盖-touchesBegan:withEvent:,以便确定每个触摸是否在排除的子视图中开始,并-ignoreTouch:forEvent:在触摸时调用该触摸。

显然,您还需要添加一个属性来跟踪排除的子视图,或者更好地跟踪排除的子视图的数组。



2

不继承任何类是可能的。

您可以在手势的回调选择器中检查gestureRecognizers

如果view.gestureRecognizers不包含您的poseRecognizer,请忽略它

例如

- (void)viewDidLoad
{
    UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self     action:@selector(handleSingleTap:)];
    singleTapGesture.numberOfTapsRequired = 1;
}

在这里检查view.gestureRecognizers

- (void)handleSingleTap:(UIGestureRecognizer *)gestureRecognizer
{
    UIEvent *event = [[UIEvent alloc] init];
    CGPoint location = [gestureRecognizer locationInView:self.view];

    //check actually view you hit via hitTest
    UIView *view = [self.view hitTest:location withEvent:event];

    if ([view.gestureRecognizers containsObject:gestureRecognizer]) {
        //your UIView
        //do something
    }
    else {
        //your UITableView or some thing else...
        //ignore
    }
}

如其他答案中所述,如果使用此方法,请确保使用以下方法来确保轻击手势识别器将轻击转发到您的视图层次结构:singleTapGesture.cancelsTouchesInView = NO;添加到viewDidLoad上方
Nick Ager


1

为parentView的所有识别器实现一个委托,并在负责同时触发识别器的委托中放置gestureRecognizer方法:

func gestureRecognizer(UIGestureRecognizer,       shouldBeRequiredToFailByGestureRecognizer:UIGestureRecognizer) -> Bool {
if (otherGestureRecognizer.view.isDescendantOfView(gestureRecognizer.view)) {
    return true
    } else {
    return false
}

}

如果要触发子级而不是父级识别器,则可以使用fail方法:

https://developer.apple.com/reference/uikit/uigesturerecognizerdelegate


0

我也在做一个弹出窗口,这就是我的做法

func didTap(sender: UITapGestureRecognizer) {

    let tapLocation = sender.locationInView(tableView)

    if let _ = tableView.indexPathForRowAtPoint(tapLocation) {
        sender.cancelsTouchesInView = false
    }
    else {
        delegate?.menuDimissed()
    }
}

0

您可以将其关闭和打开。...在我的代码中,我做了类似的事情,因为我需要在不显示键盘时将其关闭,您可以将其应用于您的情况:

称这是viewdidload等:

NSNotificationCenter    *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(notifyShowKeyboard:) name:UIKeyboardDidShowNotification object:nil];
[center addObserver:self selector:@selector(notifyHideKeyboard:) name:UIKeyboardWillHideNotification object:nil];

然后创建两个方法:

-(void) notifyShowKeyboard:(NSNotification *)inNotification 
{
    tap.enabled=true;  // turn the gesture on
}

-(void) notifyHideKeyboard:(NSNotification *)inNotification 
{
    tap.enabled=false;  //turn the gesture off so it wont consume the touch event
}

这是禁用水龙头。我不得不将tap变成一个实例变量,然后在dealloc中释放它。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.