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

发布于 2024-10-20 20:20:55 字数 759 浏览 6 评论 0原文

我正在尝试弄清楚如何以正确的方式完成此操作。我试图描述这种情况: 在此处输入图像描述

我正在添加 UITableView 作为 UIView 的子视图UIView 响应点击和 pinchGestureRecognizer,但是当这样做时,tableview 停止对这两个手势做出反应(它仍然对滑动做出反应)。

我已经使它与以下代码一起工作,但这显然不是一个好的解决方案,我确信有更好的方法。这被放入 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];
}

I'm trying to figure out how this is done the right way. I've tried to depict the situation:
enter image description here

I'm adding a UITableView as a subview of a UIView. The UIView responds to a tap- and pinchGestureRecognizer, but when doing so, the tableview stops reacting to those two gestures (it still reacts to swipes).

I've made it work with the following code, but it's obviously not a nice solution and I'm sure there is a better way. This is put in the UIView (the superview):

-(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];
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

遇到 2024-10-27 20:20:55

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

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

I had a very similar problem and found my solution in this SO question. In summary, set yourself as the delegate for your UIGestureRecognizer and then check the targeted view before allowing your recognizer to process the touch. The relevant delegate method is:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
       shouldReceiveTouch:(UITouch *)touch
若言繁花未落 2024-10-27 20:20:55

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

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

The blocking of touch events to subviews is the default behaviour. You can change this behaviour:

UITapGestureRecognizer *r = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(agentPickerTapped:)];
r.cancelsTouchesInView = NO;
[agentPicker addGestureRecognizer:r];
逆夏时光 2024-10-27 20:20:55

我正在显示一个具有自己的表格视图的下拉子视图。因此,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;
}

I was displaying a dropdown subview that had its own tableview. As a result, the touch.view would sometimes return classes like UITableViewCell. I had to step through the superclass(es) to ensure it was the subclass I thought it was:

-(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;
}
沉默的熊 2024-10-27 20:20:55

以 @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)
    }
}

Building on @Pin Shih Wang answer. We ignore all taps other than those on the view containing the tap gesture recognizer. All taps are forwarded to the view hierarchy as normal as we've set tapGestureRecognizer.cancelsTouchesInView = false. Here is the code in 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)
    }
}
も星光 2024-10-27 20:20:55

一种可能性是子类化您的手势识别器(如果您还没有)并覆盖 -touchesBegan:withEvent: ,以便确定每次触摸是否在排除的子视图中开始并调用 -ignoreTouch: forEvent: 对于该触摸(如果有)。

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

One possibility is to subclass your gesture recognizer (if you haven't already) and override -touchesBegan:withEvent: such that it determines whether each touch began in an excluded subview and calls -ignoreTouch:forEvent: for that touch if it did.

Obviously, you'll also need to add a property to keep track of the excluded subview, or perhaps better, an array of excluded subviews.

不知所踪 2024-10-27 20:20:55

不继承任何类也是可以的。

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

如果view.gestureRecognizers不包含你的gestureRecognizer,

,只需忽略它,例如

- (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
    }
}

It is possible to do without inherit any class.

you can check gestureRecognizers in gesture's callback selector

if view.gestureRecognizers not contains your gestureRecognizer,just ignore it

for example

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

check view.gestureRecognizers here

- (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
    }
}
柏林苍穹下 2024-10-27 20:20:55

我创建了一个 UIGestureRecognizer 子类,旨在阻止附加到特定视图的超级视图的所有手势识别器。

这是我的 WEPopover 项目的一部分。您可以在此处找到它。

I created a UIGestureRecognizer subclass designed for blocking all gesture recognizers attached to a superviews of a specific view.

It's part of my WEPopover project. You can find it here.

热情消退 2024-10-27 20:20:55

为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

implement a delegate for all the recognizers of the parentView and put the gestureRecognizer method in the delegate that is responsible for simultaneous triggering of recognizers:

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

U can use the fail methods if u want to make the children be triggered but not the parent recognizers:

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

软的没边 2024-10-27 20:20:55

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

func didTap(sender: UITapGestureRecognizer) {

    let tapLocation = sender.locationInView(tableView)

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

I was also doing a popover and this is how I did it

func didTap(sender: UITapGestureRecognizer) {

    let tapLocation = sender.locationInView(tableView)

    if let _ = tableView.indexPathForRowAtPoint(tapLocation) {
        sender.cancelsTouchesInView = false
    }
    else {
        delegate?.menuDimissed()
    }
}
江挽川 2024-10-27 20:20:55

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

调用这是 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 中释放它。

You can turn it off and on.... in my code i did something like this as i needed to turn it off when the keyboard was not showing, you can apply it to your situation:

call this is viewdidload etc:

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

then create the two methods:

-(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
}

What this does is disables the tap. I had to turn tap into a instance variable and release it in dealloc though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文