捕获超视图框架外的子视图(多层)

时间:2017-03-21 13:29:25

标签: ios objective-c xamarin uiview

我遇到了类似问题:Capturing touches on a subview outside the frame of its superview using hitTest:withEvent:

场合

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

为了实现更好的模块化,我们有另一个名为LabeledContainer的自定义视图。在其中,我可以设置标签并向底部视图添加内容,在这种情况下是autoComplete。

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

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

问题

我遇到“hitarea”问题,因为autoComplete下拉列表(tableView)位于自定义视图框架之外。框架只占用输入字段的高度,下拉框架在底部重叠。所以我添加了以下方法来将下拉列表添加到hitArea。

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

但是这只有在我将autoComplete添加为mainView的subChild时才有效,因为PointInside会在那里被触发。如果我将autoComplete添加到labeledContainer,后者的标签高度为自动完成的input + inputfield,则PointsInnside方法永远不会被触发。情况就是这样,因为带标签的容器不够高,对吧?那么当superView被触摸时,一个视图的PointsInside会被触发?但我不能使得labeledContainer或autoComplete更高,以防止推送其他视图。

我尝试将PointsInside方法也添加到labeledContainer,但它没有解决我的问题,因为它没有触及labeledContainer帧,并且从不调用autoComplete PointsInside。

2 个答案:

答案 0 :(得分:4)

默认情况下,UIViews不会在其超级视图之外接收触摸事件。但是,您可以将容器视图子类化以检测其框架外的触摸。容器视图应将未处理的触摸事件转发到其子视图。

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

来自Apple's documentation

  

接收器范围之外的点永远不会被报告为   点击,即使他们实际上位于接收者的子视图之一。   如果设置了当前视图的剪辑To Bounds属性,则会发生这种情况   为NO,受影响的子视图超出了视图范围。

答案 1 :(得分:0)

巴图,非常感谢你的帮助。我现在在你的建议的帮助下找到了类似的解决方案。

我没有直接在下拉范围内测试命中,而是将hitTest委托给自定义自动完成视图。所以我没有公开dropdown属性,可以在我的自定义自动完成视图中测试它。

// 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);
}

此外,我必须像这样实现autoComplete的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来处理输入字段点击。