UIView、其父视图和touchesBegan:方法

6
假设我有一个UIViewController的子类,它负责一些UIView。这些UIView被添加为UIViewController的view属性的子视图:

UIViewController:

- (void)viewDidLoad {
    UIImageView *smallImageView...
    [self.view addSubview:smallImageView];

    UIButton *button..
    [self.view addSubview:button];

    UIView *bigUIView.. // covers the entire screen (frame = (0.0, 0.0, 1024.0, 768.0))
    [self.view addSubview:bigUIView];
...
}

据我所知,由于bigUIView是最前面的视图并覆盖整个屏幕,它将接收touchesBegan:withEvent:和其他视图,例如button将不会接收任何触摸事件。
在我的应用程序中,bigUIView必须是最顶层的视图,因为它包含主要的用户界面对象(实际上是CALayers,即主要的游戏对象),当这些对象进行动画处理时,它们必须位于所有其他次要UI元素(如UIButtons等)之上。但是,我仍然希望能够在响应链中拥有UIButtons和其他对象。
我尝试在bigUIView类中实现以下内容:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {    
    [self.superview touchesBegan:touches withEvent:event];

    ... hit test the touch for this specific uiview..
}

注:

  1. bigUIView 的父视图是指 UIViewController 的 view 属性,它是否会将触摸事件传递给所有子视图?
  2. bigUIView 必须设置 userInteractionEnabled = YES,因为它还处理用户输入。
  3. 我无法将 button/smallImageView 带到前面,因为它会出现在主游戏对象(bigUIView 的子层)的上方。

提前感谢。

2个回答

12

尝试在bigUIView类中覆盖pointInside:withEvent:方法,如果触摸点位于背景视图的其他子视图的框架矩形内,则返回NO。由于它是这些其他视图的同级视图,因此命中测试机制将继续测试其他子视图,并找到对应于用户触摸点的那个子视图。

另外,考虑在您的UIViewController子类中实现事件阶段方法(例如-touchesBegan:withEvent:等),而不是在bigUIView类中实现。iOS会在响应者链中将视图控制器插入其视图后面,因此事件消息将冒泡到视图控制器,除非其中一个其他子视图(例如按钮)拦截了它。然后,视图控制器可以向其任何视图发送任何消息。


谢谢jlehr,pointInside:withEvent:是解决方案。当点在其他子视图的框架矩形内时返回NO,这会导致bigUIView-touchesBegan:withEvent:方法不被调用,而触发button的操作。请注意,这会导致bigUIViewbutton之间产生耦合。当bigUIView覆盖整个屏幕时,我如何仅在UIViewController中拦截事件阶段方法?在bigUIView类中创建一个不同的方法签名,并由“catch all”UIViewController类委托它? - Eduardo Coelho
1
不要在 bigUIView 中实现事件阶段方法。这样,消息将会被转发到响应者链上直到它们到达具有重写实现的对象(例如 UIButton 或您自定义的 UIViewController 子类)。 - jlehr
1
关于耦合性,考虑在您的视图控制器中实现委托方法,以便bigUIView实例可以调用该方法进行实际决策。这样,视图控制器将负责计算哪个视图接收消息,完全没有任何耦合。 - jlehr
好的,但是如果我的 bigUIView 需要 -touchesBegan:withEvent: 委托方法参数才能正常工作怎么办?覆盖会导致响应链停止,您建议创建另一个事件阶段的“同级”方法签名并在 bigUIView 中实现它们(这些方法将由 UIViewController 调用)吗? - Eduardo Coelho
2
一种选择是将所有触摸事件消息通过控制器路由,然后让控制器决定响应的操作(部分通过向一个或多个视图发送消息)。另一个选择是在bigUIView中每个重写方法的末尾包含对[super touchesBegan:withEvent:]的调用,这将允许消息继续沿着响应者链传递,以便它们被bigUIView和其控制器接收。 - jlehr

0
通常在响应者链中,事件会传递给父视图而不是子视图。无论如何,我会将事件中的点转换为按钮的本地坐标系,并且如果命中,向控制器发送一个动作。

我很感谢您的评论,这是一个有趣的解决方案。然而,我正在尝试避免bigUIView(属于“game package”(CALayers))与其他在IB中创建的UI元素之间的高耦合。谢谢。 - Eduardo Coelho
1
没问题。bigUIView仍然不需要与其他UI元素一起工作。但我同意jlehr的解决方案似乎是正确的选择! - MHC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接