在多个层级中,捕获超出其父视图框架的子视图上的触摸事件

4

我在这个线程中描述了一个类似的问题:Capturing touches on a subview outside the frame of its superview using hitTest:withEvent:

情况

我们正在开发一款Xamarin iOS应用程序。我们有一个自定义视图用于创建自己的自动完成输入字段,其中包含一个UiTextField和一个UiTableView,下面展示结果。

为了实现更好的模块化,我们还有另一个自定义视图叫做LabeledContainer。在里面,我可以设置一个标签并将内容添加到底部视图,这种情况下就是自动完成。

AutoComplete               LabeledContainer
+---------------+          +---------------+
| UiView        |          | UiView        |
|+-------------+|          |+-------------+|
|| UiTextField ||          || UiLabel     ||
|+-------------+|          |+-------------+|
|+-------------+|          |+-------------+|
|| UiTableView ||          || UiView      ||
|+-------------+|          |+-------------+|
+---------------+          +---------------+

The following gets rendered:
MAINVIEW
+------------------------------+
|                              |
|                              |
|                              |
|                              |
|+----------------------------+|
|| LabeledContainer           ||
||+--------------------------+||
||| Label                    |||
||+--------------------------+||
||+--------------------------+||
||| Content                  |||
|||+------------------------+|||
|||| AutoComplete           ||||
|||+------------------------+|||
||+--------------------------+||
|+----------------------------+|
|                              |
|                              |
|                              |
|                              |
+------------------------------+

问题

我的问题在于“hitarea”,因为自动完成下拉列表(tableView)超出了自定义视图的框架。该框架只占据了输入字段的高度,而下拉列表在底部重叠。因此,我添加了以下方法来将下拉列表添加到hitArea。

public override bool PointInside(CGPoint point, UIEvent uievent)
{
    var bounds = Bounds;
    bounds.Height += DROPDOWN.Bounds.Height;   
    return bounds.Contains(point);
}

但是这只有在我将自动完成作为主视图的子视图添加时才起作用,因为这时会触发PointInside方法。如果我将自动完成添加到带有标签的容器中,该容器的框架高度后面会变成标签和自动完成输入字段的高度,那么PointsInside方法就永远不会被触发。这是因为标记容器的高度不够,对吧?那么视图的PointsInside方法只有在超级视图被触摸时才会被触发?但我无法使标记容器或自动完成更高来防止推动其他视图。
我尝试将PointsInside方法也添加到标记容器中,但它没有解决我的问题,因为它没有触碰到标记容器的框架,所以自动完成的PointsInside方法从未被调用。
2个回答

4

默认情况下,UIView 在其父视图之外不接收触摸事件。但是,您可以对容器视图进行子类化以检测其帧外的触摸事件。容器视图应将未处理的触摸事件转发到其子视图。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    CGPoint pointForTargetView = [self.autoCompleteView convertPoint:point fromView:self];

    if (CGRectContainsPoint(self.autoCompleteView.bounds, pointForTargetView)) {
        return [self.autoCompleteView hitTest:pointForTargetView withEvent:event];
    }
    return [super hitTest:point withEvent:event];
}

来自苹果文档:

位于接收器边界之外的点永远不会被报告为点击,即使它们实际上位于接收器的一个子视图中。如果当前视图的 clipsToBounds 属性设置为 NO,并且受影响的子视图超出了视图的边界,则可能会发生这种情况。


太好了,非常感谢您的解释。 - KSR

0
Batu,非常感谢您的帮助。我现在通过您的建议找到了类似的解决方案。
我没有直接测试下拉框边界内的命中,而是将hitTest代理到自定义自动完成视图。因此,我不必公开下拉框属性,可以在我的自定义自动完成视图内部进行测试。
// Xamarin version - labeledContainer
public override UIView HitTest(CGPoint p, UIEvent e)
{
    var translatedP = AutoComplete.ConvertPointFromView(p, this);
    return AutoComplete.HitTest(translatedP, e) ?? base.HitTest(p, uievent);
}

此外,我还需要像这样实现自动完成的HitTest:
// Xamarin version - autoComplete
public override UIView HitTest(CGPoint p, UIEvent e)
{
    var translatedP = DropDown.ConvertPointFromView(p, this);
    return DropDown.HitTest(translatedP, e) ?? base.HitTest(p, uievent);
}

在这里,我翻译了结果下拉菜单的点,并检查它是否在其中。如果不是,则调用默认的HitTest来处理输入字段的点击。


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