【问题标题】:Tap Gesture Recognizer attached with container view does not block touch event of button in container view but blocks toolbar button's touch event与容器视图连接的点击手势识别器不会阻止容器视图中按钮的触摸事件,但会阻止工具栏按钮的触摸事件
【发布时间】:2014-02-14 12:40:48
【问题描述】:

所以我有一个视图控制器,它有一个容器视图。容器视图嵌入了导航控制器,该控制器也是视图控制器的父控制器。故事板是这样的:

视图控制器(mainViewController) --> 导航控制器 --> 视图控制器(contentViewController)

您可以在下面看到故事板的屏幕截图。

第一个箭头是从容器视图到导航控制器的嵌入segue。第二个箭头是一个关系,代表contentViewController是导航控制器的根视图控制器。

mainViewController 和contentViewController 是同一类的对象,名为testViewController。它是 UIViewController 的子类。它的实现很简单。它只有三个IBAction 方法,没有别的。下面是实现代码:

#import "TestViewController.h"

@implementation TestViewController

- (IBAction)buttonTapped:(id)sender {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil
                                                    message:@"button is tapped"
                                                   delegate:self
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
    [alert show];
}

- (IBAction)barButtonTapped:(id)sender
{
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil
                                                    message:@"bar button is tapped"
                                                   delegate:self
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
    [alert show];
}

- (IBAction)viewTapped:(id)sender {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil
                                                    message:@"view is tapped"
                                                   delegate:self
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles: nil];
    [alert show];
}        
@end

我在mainViewController 的容器视图中添加了一个点击手势识别器。当容器视图被点击时,它会向mainViewController 发送viewTapped:(id)sender 消息。在contentViewController 的根视图内部,有一个按钮,当点击该按钮时,它会向contentViewController 发送buttonTapped:(id)sender 消息。在contentViewController 的工具栏中有一个条形按钮,点击时会向contentViewController 发送barButtonTapped:(id)sender 消息。初始场景是mainViewController。当应用程序运行时,我发现只有栏按钮的触摸事件被阻止,触摸事件被按钮正确处理。在 Apple 的文档 Regulating the Delivery of Touches to Views 中,它说:

在简单的情况下,当发生触摸时,将触摸对象传递给 从 UIApplication 对象到 UIWindow 对象。然后,窗户 首先将触摸发送到附加在视图中的任何手势识别器 触摸发生(或该视图的超级视图),在它通过之前 对视图对象本身的触摸。

我认为触摸事件不会传递给按钮。这真的让我很困惑。有人可以解释这种行为吗?非常感谢。


故事板屏幕截图:

【问题讨论】:

    标签: ios iphone event-handling uigesturerecognizer uicontainerview


    【解决方案1】:

    Event Handling Guide for iOS: Event Delivery: The Responder Chain 的“响应者链遵循特定的传递路径”部分描述了触摸事件如何首先传递到被触摸的视图,然后向上通过其所有超级视图,然后传递到窗口,最后传递到应用程序本身。

    项目视图层次结构的简化表示形式是:

    mainViewController's Root View
      | mainViewController's Container View (has Tap Gesture Recognizer)
      |   | UINavigationController's Root View
      |   |   | contentViewController's View
      |   |   |   | UIButton ("Button")
      |   |   | UINavigationController's Toolbar View
      |   |   |   | UIToolbarTextButton ("Item")
    

    ...所以当您点击按钮或工具栏按钮时,它们会在 mainViewController 的容器视图之前接收到触摸事件。

    按钮的事件触发并且工具栏按钮似乎与Event Handling Guide for iOS: Gesture Recognizers'“与其他用户界面控件交互”部分无关的原因:

    在 iOS 6.0 及更高版本中,默认控制操作可防止重叠的手势识别器行为。例如,按钮的默认操作是单击。如果您将单击手势识别器附加到按钮的父视图,并且用户点击了该按钮,则该按钮的操作方法将接收触摸事件而不是手势识别器。

    这似乎解释了为什么 UIButton 能够抢占点击手势识别器,但它没有明确说明工具栏按钮。

    如果您打印出视图层次结构,您会发现工具栏按钮使用UIToolbarButton 表示,这是一个直接继承自UIControl 的私有类。根据我们的观察,我们假设UIToolbarButton 不会像公共UIControl 子类那样抢占手势识别器。当我调整它的touchesCancelled:withEvent: 方法时,我发现它在点击手势识别器触发后被调用,这似乎是您根据 iOS 事件处理指南所期望的:手势识别器的“手势识别器获得第一个机会来识别触摸” " 他们注意到的部分:

    ...如果手势识别器识别出触摸手势,则窗口永远不会将触摸对象传递给视图,并且还会取消它之前发送给视图的任何触摸对象,这些触摸对象属于该识别序列的一部分。

    您可以通过几种不同的方式修改此行为,您选择的方式取决于您的最终目标。如果您想允许触摸工具栏,您可以检查发送给手势识别器代理的gestureRecognizer:shouldReceiveTouch: 的UITouch 是否在工具栏的框架内,如果是则返回NO。阻止对 UIButton 的触摸可能需要子类化,但如果您想阻止对 mainViewController 的子视图控制器的所有触摸,您可以在其容器视图上添加一个透明视图。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多