title: What works to pass touches through to (and display all) subviews?
body: <p>I have the following subview chain:</p>
  
  <pre><code>UIViewController.view -+
                         |-&gt; UIView (subclass) -+
                         |                      +-&gt; UIToolbar 
                         |
                         +------------------------&gt; UIWebView
  </code></pre>
  
  <p>In the subclass, I override its <code>-touchesEnded:forEvent:</code> method in order to hide and show the <code>UIToolbar</code> on a single tap touch, through a <code>CAAnimation</code>, as well as issue an <code>NSNotification</code> that causes the view controller to hide its navigation bar. </p>
  
  <p>If I do not add the <code>UIWebView</code> as a subview to the view controller's view, then this works properly.</p>
  
  <p>If I then add the <code>UIWebView</code> as a subview of the view controller's view, then the <code>UIToolbar</code> does not appear, and I do not get the animation effect. The <code>UIWebView</code> responds to touches but the subclassed <code>UIView</code> does not. The notification does get fired, though, and the navigation bar does get hidden.</p>
  
  <p>What is the best way to arrange these subviews so that:</p>
  
  <ol>
  <li>The <code>UIToolbar</code> can be made to slide on and off the screen</li>
  <li>The <code>UIWebView</code> is visible can still receive its typical touch-events: zoom in/out and double-tap to reset zoom level</li>
  </ol>
  
  <p>A correct answer will meet both criteria.</p>
  
  <hr>
  
  <p><strong>EDIT</strong></p>
  
  <p>I made some progress with this, but I cannot distinguish touch events and thus I fire a toolbar hide/show method when it should not be fired.</p>
  
  <p>I set the view controller's view property to the <code>UIView</code> subclass, and I inserted the web view at the top of the <code>-subviews</code> array via <code>[self.view insertSubview:self.webView atIndex:0]</code>.</p>
  
  <p>I added the following method to the <code>UIView</code> subclass:</p>
  
  <pre><code>- (UIView *) hitTest:(CGPoint) point withEvent:(UIEvent *)event {
      UIView *subview = [super hitTest:point withEvent:event];
  
      if (event.type == UIEventTypeTouches) {
          [self toggleToolbarView:self];
  
      // get touches
      NSSet *touches = [event allTouches];
  
      NSLog(@"subview: %@", subview);
      NSLog(@"touches: %@", touches);
  
      // individual touches
      for (UITouch *touch in touches) {
          switch (touch.phase) {
              case UITouchPhaseBegan:
                  NSLog(@"UITouchPhaseBegan");
                  break;
              case UITouchPhaseMoved:
                  NSLog(@"UITouchPhaseMoved");
                  break;
              case UITouchPhaseCancelled:
                  NSLog(@"UITouchPhaseCancelled");
                  break;
              case UITouchPhaseStationary:
                  NSLog(@"UITouchPhaseStationary");
                  break;      
              default:
                  NSLog(@"other phase...");
                  break;
              }
          }
      }
  
      return subview;
  }
  </code></pre>
  
  <p>The method <code>-toggleToolbarView:</code> triggers an <code>NSTimer</code>-timed <code>CAAnimation</code> that hides/shows a <code>UIToolbar*</code>.</p>
  
  <p>The problem is that a touch-drag combination causes <code>-toggleToolbarView:</code> to be fired (as well as zooming in or out of the web view). What I would like to do is cause <code>-toggleToolbarView:</code> to be fired only when there is a touch-only event.</p>
  
  <p>When I call the above method <code>-hitTest:withEvent:</code>, it looks like the <code>UIWebView</code> sucks up the touches. The two <code>NSLog</code> statements show that the <code>UIView*</code> that is returned from the parent <code>UIView*</code>'s <code>-hitTest:withEvent:</code> is either the <code>UIWebView</code> or the <code>UIToolbar</code> (when it is on-screen and touched). The <code>touches</code> array is empty and therefore the touch event types cannot be distinguished.</p>
  
  <p>There's one more thing I will try, but I wanted to record this attempt here in case others have quick suggestions. Thanks for your continued help.</p>
  
  <hr>
  
  <p><strong>EDIT II</strong></p>
  
  <p>I made some progress with getting the <code>UIWebView</code> to pinch/zoom and respond to double-taps. I can also hide/show the toolbar with single-taps. </p>
  
  <p>Instead of messing with the <code>UIWebView</code> instance's private <code>UIScroller</code>, I access its inner, private <code>UIWebDocumentView</code>. But the web view as a whole does not redraw properly after zooming in.</p>
  
  <p>What this means: If my document contains a PDF or an SVG vector illustration, for example, zooming in causes the contents to be blurry, as if the image tiles that make up the rendered PDF or vector illustration contents are not being re-rendered at the new zoom factor.</p>
  
  <p>For the purposes of documenting this, I will:</p>
  
  <ol>
  <li><p>Call the <code>UIView (subclass)</code> instead <code>ViewerOverlayView</code></p></li>
  <li><p>The <code>UIWebView</code> is called <code>ViewerWebView</code> </p></li>
  </ol>
  
  <p>Both are subclasses of <code>UIView</code> and <code>UIWebView</code>, respectively.</p>
  
  <p>My <code>ViewerWebView</code> contains a new ivar (with <code>@property</code> + <code>@synthesized</code>):</p>
  
  <pre><code>UIView *privateWebDocumentView;
  </code></pre>
  
  <p>(This <code>UIView</code> is actually a pointer to a <code>UIWebDocumentView</code> instance, but to avoid Apple flagging the app for refusal I cast this to a <code>UIView</code> for the simple purpose of passing it touch events.)</p>
  
  <p>The implementation of <code>ViewerWebView</code> overrides <code>-hitTest:withEvent:</code></p>
  
  <pre><code>- (UIView *) hitTest:(CGPoint)point withEvent:(UIEvent *)event {
      UIView *_subview = [super hitTest:point withEvent:event];
      self.privateWebDocumentView = _subview;
      return _subview;
  }
  </code></pre>
  
  <p>My <code>ViewerOverlayView</code> contains new ivars (with <code>@property</code> + <code>@synthesized</code>):</p>
  
  <pre><code>ViewerWebView *webView;
  UIView *webDocumentView;
  </code></pre>
  
  <p>In the implementation of the overlay view, I override <code>-touchesBegan:withEvent:</code>, <code>-touchesMoved:withEvent:</code> and <code>-touchesEnded:withEvent:</code></p>
  
  <pre><code>- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
      self.touchHasMoved = NO;
      if (self.webView) {
          UITouch *touch = [touches anyObject];
          CGPoint touchPoint = [touch locationInView:self];
          self.webDocumentView = [self.webView hitTest:touchPoint withEvent:event];
      }
      [super touchesBegan:touches withEvent:event];
      [self.webDocumentView touchesBegan:touches withEvent:event];
  }
  
  - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
      self.touchHasMoved = YES;
      if (self.webView) {
          UITouch *touch = [touches anyObject];
          CGPoint touchPoint = [touch locationInView:self];
          self.webDocumentView = [self.webView hitTest:touchPoint withEvent:event];
      }
      [super touchesMoved:touches withEvent:event];
      [self.webDocumentView touchesMoved:touches withEvent:event];
  }
  
  - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
      if(!self.touchHasMoved) 
          [self toggleToolbarView:self];
      if (self.webView) {
          UITouch *touch = [touches anyObject];
          CGPoint touchPoint = [touch locationInView:self];
          self.webDocumentView = [self.webView hitTest:touchPoint withEvent:event];
      }
      [super touchesEnded:touches withEvent:event];
      [self.webDocumentView touchesEnded:touches withEvent:event];
  }
  </code></pre>
  
  <p>In the view controller, I do the following:</p>
  
  <ol>
  <li><p>Instantiate the <code>ViewerOverlayView</code> and add it as a subview of the view controller's <code>view</code> property</p></li>
  <li><p>Instantiate the view controller's <code>ViewerWebView</code> instance and insert it at the bottom of the subviews array</p></li>
  <li><p>Set the <code>ViewerOverlayView</code> web view property to the view controller's web view</p></li>
  </ol>
  
  <p>So now my transparent <code>ViewerOverlayView</code> correctly passes zoom/stretch/double-tap touch events to the <code>ViewerWebView</code> instance's private <code>UIWebDocumentView</code> instance. The <code>UIWebDocumentView</code> then resizes. </p>
  
  <p>However, the content does not redraw itself with the new scale. So vector-based content (e.g. PDF, SVG) looks blurry when zooming in.</p>
  
  <p>Here's what the view looks like without scaling, nice and sharp:</p>
  
  <p><a href="http://www.flickr.com/photos/alexreynolds/4191817515/" rel="nofollow noreferrer" title="UIWebView, no scaling by Alex Reynolds, on Flickr"><img src="https://farm3.static.flickr.com/2529/4191817515_76cfb8e022_o.png" width="386" height="742" alt="UIWebView, no scaling" /></a></p>
  
  <p>Here's what the view looks like when zoomed in, which is not as sharp as it would otherwise be without all this custom event munging:</p>
  
  <p><a href="http://www.flickr.com/photos/alexreynolds/4191817517/" rel="nofollow noreferrer" title="UIWebView, blurry zoom by Alex Reynolds, on Flickr"><img src="https://farm3.static.flickr.com/2553/4191817517_959a8ba697_o.png" width="386" height="742" alt="UIWebView, blurry zoom" /></a></p>
  
  <p>Interestingly, I can only rescale between two zoom levels. Once I stop double-tap-stretching to zoom in at any level, it then "snaps back" to the frame you see in the image above.</p>
  
  <p>I tried <code>-setNeedsDisplay</code> on the <code>ViewerWebView</code> and its inner <code>UIWebDocumentView</code> instance. Neither method call worked.</p>
  
  <p>Despite wanting to avoid private methods, because I want to eventually get this app approved, I also tried <a href="http://pinchzoom.com/blog/items/view/1386/one-of-the-problems-with-the-uikit-at-the-moment-is-an-issue-embedding-a-uiwebview-within-a-table" rel="nofollow noreferrer">this suggestion</a> of accessing the private <code>UIWebDocumentView</code> method <code>-_webCoreNeedsDisplay</code>:</p>
  
  <pre><code>[(UIWebDocumentView *)self.webDocumentView _webCoreNeedsDisplay];
  </code></pre>
  
  <p>This method caused the app to crash from an <code>unrecognized selector</code> exception. Perhaps Apple has changed the name of this method from SDK 3.0 to 3.1.2.</p>
  
  <p>Unless there is a way to cause the web view to redraw its contents, I guess I'm going to have to scrap the transparent overlay view and just draw a web view, in order to get the web view to work properly. That sucks!</p>
  
  <p>If anyone has thoughts about redrawing-after-scaling, please feel free to add an answer.</p>
  
  <p>Thanks again to all of you for your input. As an aside, I did not cancel the bounty on this question -- I do not know why it was not automatically awarded to the first answer to this thread, as I was otherwise expecting to happen from previous experience.</p>
  
  <hr>
  
  <p><strong>EDIT III</strong></p>
  
  <p>I found this web page that shows <a href="http://mithin.in/2009/08/26/detecting-taps-and-events-on-uiwebview-the-right-way/" rel="nofollow noreferrer">how to subclass the application window</a> so as to observe taps and forward those taps to the view controller.</p>
  
  <p>It's not necessary to implement its delegate everywhere throughout the application, only where I want to observe taps on <code>UIWebView</code>, so this may work really well for a document viewer.</p>
  
  <p>So far, I can observe taps, hide and show the toolbar, and the <code>UIWebView</code> behaves like a web view. I can zoom in and out of the web view and the document is re-rendered properly.</p>
  
  <p>While it would be nice to learn how to manage taps in a more generic fashion, this looks like a pretty great solution.</p>