简体   繁体   中英

Capturing touches on a subview outside the frame of its superview (over multiple layers)

I have a similar problem described in this thread: Capturing touches on a subview outside the frame of its superview using hitTest:withEvent:

Situation

We are developing an app in Xamarin iOS. We have a custom view to create an own autoComplete input field, containing an UiTextField and an UiTableView beneath to show the results.

To achieve a better modularity we have another custom view called LabeledContainer. Inside it, I can set a label and add content to the bottom view, which is the autoComplete in this case.

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

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

Problem

I have problems with the "hitarea" because the autoComplete dropdown (tableView) is outside of the custom views frame. The frame just takes the height of the input field and the dropdown overlaps it at the bottom. So I added the following method to add the dropdown to the hitArea.

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

But this only works if I add my autoComplete as subChild of the mainView, because PointInside gets triggered there. If I add the autoComplete to the labeledContainer, which has afterwards a frame height of label + inputfield of the autocomplete, the method PointsInside gets never triggered. This is the case because the labeledContainer isn't high enough, right? So PointsInside of a view just gets triggered when the superView gets touched? But I can't make the labeledContainer or autoComplete higher to prevent pushing other views.

I tried to add the PointsInside method also to the labeledContainer, but it didn't solve my problem, because its not touching the labeledContainer frame and the autoComplete PointsInside is never called.

By default, UIViews do not receive touch events outside of their superviews. However, you can subclass your container view to detect touches outside of its frame. Container view should forward unhandled touch events to its subviews.

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

From Apple's documentation :

Points that lie outside the receiver's bounds are never reported as hits, even if they actually lie within one of the receiver's subviews. This can occur if the current view's clips​To​Bounds property is set to NO and the affected subview extends beyond the view's bounds.

Batu, thank you very much for your help. I now came to a similar solution with help of your suggestion.

I am not testing the hit directly in the dropdowns bounds, but delegate the hitTest to the custom autocomplete view. So I dont have to make the dropdown property public and can test it inside my custom autocomplete view itself.

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

Furthermore I have to implement the HitTest of the autoComplete like this:

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

Here I translate the point for the result dropdown and check if its inside it. If not, the default HitTest is called to handle the input field click.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM