简体   繁体   中英

Pass through touches to portion of UIWebView

I have a UIWebView. Using something like this:

http://blog.evandavey.com/2009/02/how-to-make-uiwebview-transparent.html

.. I have made the UIWebView transparent. I now need to be able to pass though touches on the webview to the view below, but only on a rectangular portion of the web view.

So, imagine the webview is a square, and it has a square area in the centre which must pass-thru touches to the view below.

Is there a way to do this?

This is a more specific form of hitTest manipulation.

First, create a subclass of UIView . I called mine ViewWithHole .

In its header, define a property for a UIView that will be the hole through the outer view. Make this an IBOutlet .

@property (retain) IBOutlet UIView *holeView;

Then override hitTest . If the point is returns a hit within the hole, return that. Otherwise return what it usually would.

- (UIView*) hitTest:(CGPoint)point withEvent:(UIEvent *)event;
{
    CGPoint pointInHole = [self convertPoint:point toView:self.holeView];
    UIView *viewInHole = [self.holeView hitTest:pointInHole withEvent:event];

    if (viewInHole)
        return viewInHole;
    else
        return [super hitTest:point withEvent:event];
}

In Interface Builder, or programmatically, place a ViewWithHole . Put a UIView and a UIWebView within it, in that order. Make the UIView the holeView of the ViewWithHole .

Interface Builder 配置 ViewWithHole

The holeView can itself be interactive, or you can put any number of interactive views within it. Here, I put a few standard views in there to make sure they work. Any taps on within the hole will be sent to it and its subviews, not the UIWebView on top. Any taps outside the holeView will be received by the UIWebView as usual.

You can have any number and type of views displayed in front of the hole; all that matters is that the holeView is properly connected.

my firsth idea is something like this:

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch * touch = (UITouch *)[touches anyObject];
CGPoint location = [touch locationInView:self];

//min_x,max_x,min_y,max_y are the min and max coord of your rect below

if (CGRectContainsPoint(CGRectMake(min_x,min_y,max_x,max_y),location))
{
     //send the touches to the view below
}

}

this should work if the view below is stationary but you haven't given any information about the view below (if there's buttons, or if it moves..stuff like that) so...yea..do that

You could create a subclass of UIWebView and override its hitTest:withEvent: and pointInside:withEvent: to disregard the 'hole' - but Apple say you shouldn't subclass UIWebView .

Or, you could create a UIView subclass and place the UIWebView within it as a subview, then make its hitTest:withEvent: method return views beneath the UIWebView.

Here is a generic hitTest method for a UIView subclass, that gives priority to any interactive subviews that aren't UIWebView, even if they're underneath the UIWebView. It doesn't have any specific 'hole' detection, but hitTest is also where you want to do that. Just work out if the point is in the hole before disregarding the UIWebView.

// - (UIView*) hitTest:(CGPoint)point withEvent:(UIEvent *)event;
// This overrides the usual hitTest method to give priority to objects that are not in a chosen class.
// For this example, that class has been hard-coded to UIWebView.
// This allows the webview to be displayed over other interactive components.
// For the UIWebView case, it would be nice to look at its layout and only return views beneath it in locations where it is transparent, but that information is not obtainable.
- (UIView*) hitTest:(CGPoint)point withEvent:(UIEvent *)event;
{
    UIView *viewFound = nil;
    Class lowPriorityClass = [UIWebView class];
    NSMutableArray *lowPriorityViews = [NSMutableArray array];

    // Search the subviews from front to back.
    NSEnumerator *viewEnumerator = [[self subviews] reverseObjectEnumerator];
    UIView *subview;
    while ((subview = [viewEnumerator nextObject]) && (!viewFound))
    {
        // Save low-priority cases for later.
        if ([subview isKindOfClass:lowPriorityClass])
            [lowPriorityViews addObject:subview];
        else
        {
            CGPoint pointInSubview = [self convertPoint:point toView:subview];
            if ([subview pointInside:pointInSubview withEvent:event])
            {
                // Subviews may themselves have subviews to return.
                viewFound = [subview hitTest:pointInSubview withEvent:event];
                // However, if not, return the subview itself.
                if (!viewFound)
                    viewFound = subview;
            }
        }
    }

    if (!viewFound)
    {
        // At this point, no other interactive views were found, so search the low-priority cases previously stored to see if they apply.
        // Since the lowPriorityViews are already in front to back order, enumerate forward.
        viewEnumerator = [lowPriorityViews objectEnumerator];
        while ((subview = [viewEnumerator nextObject]) && (!viewFound))
        {
            CGPoint pointInSubview = [self convertPoint:point toView:subview];
            if ([subview pointInside:pointInSubview withEvent:event])
            {
                // Subviews may themselves have subviews to return.
                viewFound = [subview hitTest:pointInSubview withEvent:event];
                // However, if not, return the subview itself.
                if (!viewFound)
                    viewFound = subview;
            }
        }
    }

    // All cases dealt with, return whatever was found.  This will be nil if no views were under the point.
    return viewFound;
}

I think that there is a very easy solution to your problem... You do not specify it, but I deduce that you don't want the user to interact with the webview HTML. If that is so, just disable UserInteractionEnabled on the webview and the touches will pass trough.

Then you need your active view located in the center as you mentioned.

I hope it helps, if you need any clarification just comment it.

I have another idea: get the global click. When someone click in iPhone, it send a message to global sendEvent . So you listen to it and do the task you want.

To do this, you need to subclass the UIApplication . First, in main.c use this function

    int retVal = UIApplicationMain(argc, argv, @"myUIApplicationClass" ,nil);

then implement this class:

@implementation BBAplicationDebug

- (void)sendEvent:(UIEvent *)event
{
    [super sendEvent:event];
    NSSet *touches = [event allTouches];
    UITouch *touch = touches.anyObject;
    [[NSNotificationCenter defaultCenter] postNotificationName:@"userClick" object:touch];
}

@end

Now you listen this NSNotification and do all the checks: Is in the correct frame, has the correct UIView, etc, etc...

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