+1  A: 

The way I'm reading your question, you want to intercept the events to trigger some action (animate toolbar, post notification), while allowing the event to also reach its natural destination.

If I were trying to do this, I would put the UIToolbar directly as a subview of the UIViewController.view. The UIWebView remains a direct subview also.

The UIViewController.view should be a subclass of UIView, and it needs to override

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

The view can side-step being part of the event processing by sending back the view that you want to receive the event (UIToolbar or UIWebView), while you still get a chance to trigger the actions you want.

An example might be:

- (UIView *)hitTest:(CGPoint) point withEvent:(UIEvent *)event {
    UIView* subview = [super hitTest:point withEvent:event];
    /* Use the event.type, subview to decide what actions to perform */
    // Optionally nominate a default event recipient if your view is not completely covered by subviews
    if (subview == self) return self.webViewOutlet;
    return subview;
}
ohhorob
Keeping the views in separate subclasses is a requirement for a correct answer.
Alex Reynolds
In that case, can you put the `UIWebView` inside the `UIView (subclass)` in your diagram above? That would also allow you to override `-hitTest:` and use the same approach.
ohhorob
Is there a way to segregate `event.type` to know what kind of touch is taking place? For example, a touch-and-drag should only move the web view, but this is also treated like a touch and this triggers the subview containing the toolbar. Is there a way to distinguish between "touch-and-drag" and plain "touch"? There is `if (event.type == UIEventTypeTouches)` but I don't see any other event type or subtype that breaks this down further.
Alex Reynolds
I'm not proficient with the touch event deciphering, but this SO answer looks appropriate: http://stackoverflow.com/questions/990870/handling-touches-inside-uiwebview/1755375#1755375
ohhorob
+1  A: 

If you start by adding the UIView subclass to the view controller's view, then add the UIWebView as you describe, the UIWebView is going to completely cover the UIView subclass (assuming both views are completely overlapping as you mention in the question comments).

It sounds like you don't want that - you want the subclass above the web view. You need to either add them to the view controller's view in the opposite order (add web view, then add custom UIView) or call:

[viewContoller.view insertSubview:webView belowSubview:subclass];

With that out of the way, let's talk about how to intercept the touch events. I'm going to suggest a different approach that allowed me to do something kind of similar. Here's the question and answer I created for it - feel free to look there for more details on this technique.

The idea is that you subclass UIApplication and override the -sendEvent: method. Yours will look something like this, I suppose:

- (void)sendEvent:(UIEvent *)event {
    [super sendEvent:event];

    NSSet *allTouches = [event allTouches];
    if ([allTouches count] > 0) {
        UITouch *touch = [allTouches anyObject];
        if ([allTouches count] == 1 && touch.phase == UITouchPhaseEnded && touch.tapCount == 1) {
            // Do something here
        }
    }
}

I don't expect this will get you 100% there, but hopefully it's a start. In particular, you may want to play around with the if condition that checks the properties of the UITouches you're dealing with. I'm not sure if I'm including the right checks or if they'll catch the exact condition you're looking for.

Good luck!

Mike McMaster
I am inserting the web view into the view controller `-subviews` array at index 0 to get it to become visible and responsive to touches. However, this causes the subview containing the toolbar to become unresponsive.
Alex Reynolds
You need to use addSubview: or one of the insertSubview: methods. Don't add the view to the subviews array directly.
Mike McMaster
I have used both methods. I don't think I want to subclass the application delegate, since this component that requires subclassing is only a small (if important) part of the application as a whole.
Alex Reynolds
+1  A: 

Okay, what about this:

-In your custom UIView subclass, add a pointer to your UIWebView

-Add an ivar to the UIView subclass, BOOL hasMoved;

-In the UIView subclass, override the touch methods like this:

-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    hasMoved = NO;
    [webView touchesBegan:touches withEvent:event];
}

-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    hasMoved = YES;
    [webView touchesMoved:touches withEvent:event];
}

-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    if(!hasMoved) [self hideToolbar];
    [webView touchesEnded:touches withEvent:event];
}

And then put the custom view on top of the web view. Seems too simple, and it probably is -- some magic may be required to handle multiple touches correctly, and you may need to use a NSTimer in the touchesBegan: method to as delay to handle double tap situations, but I think the general idea is sound...perhaps?

Ian Henry
In doing this, the application hangs with an infinite loop at `-touchesBegan:withEvent:` once the first touch occurs, which prompts a force-quit. I'm surprised this is so difficult!
Alex Reynolds
That's very strange. I can't imagine why. I set up a demo project to verify this and I didn't encounter an infinite loop -- however, it didn't work as expected. I've found another half solution which I'll post for historic purposes.
Ian Henry
+1  A: 

Take 2: same as above, but override the methods as follows:

-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    hasMoved = NO;
    [[[webView subviews] objectAtIndex:0] touchesBegan:touches withEvent:event];
}

-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    hasMoved = YES;
    [[[webView subviews] objectAtIndex:0] touchesMoved:touches withEvent:event];
}

-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    if(!hasMoved) [self toggleToolbar];
    [[[webView subviews] objectAtIndex:0] touchesEnded:touches withEvent:event];
}

-(void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [[[webView subviews] objectAtIndex:0] touchesCancelled:touches withEvent:event];
}

This works...sort of. This allows you to drag the web view around. But it isn't handling other touch events properly (following links, pinching, anything). So. Probably not very useful, but it was progress to me!

Ian Henry
I rearranged views and noticed that this subview will accept drags. No zooming, though.
Alex Reynolds