1

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

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

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

第一个箭头是从容器视图到导航控制器的嵌入序列。第二个箭头是一个关系,代表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. 它在容器视图被点击时发送viewTapped:(id)sender消息。mainViewController在 的根视图内部contentViewController,有一个按钮,点击时buttonTapped:(id)sender会向其发送消息contentViewController。工具栏中有一个条形按钮,点击时会向其contentViewController发送barButtonTapped:(id)sender消息contentViewController。最初的场景是mainViewController。当应用程序运行时,我发现只有栏按钮的触摸事件被阻止,触摸事件被按钮正确处理。在 Apple 的文档Regulating the Delivery of Touches to Views中,它说:

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

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


故事板截图故事板

4

1 回答 1

4

iOS的事件处理指南:事件传递:响应者链的“响应者链遵循特定的传递路径”部分描述了触摸事件如何首先传递到被触摸的视图,然后向上传递到其所有超级视图,然后传递到窗口,最后是应用程序本身。

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

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 的容器视图之前收到触摸事件。

按钮事件触发且工具栏按钮似乎与iOS 事件处理指南无关的原因:手势识别器的“与其他用户界面控件交互”部分:

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

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

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

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

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

于 2014-09-27T22:53:09.450 回答