17

I want to have a UIWebView inside a UIScrollView. The UIWebView will sometimes go out of bounds of the iPhone screen so I need a way to scroll the view so I can see all the content (but I don't want to use the built in scrolling of the UIWebView). So I'm thinking of putting all the content inside of a UIScrollView and then making the height of the UIScrollView to equal the height of the UIWebView and other views that are in it.

Here's an image to help describing my problem:

Interface Builder Image

Peter Warbo
  • 11,136
  • 14
  • 98
  • 193

1 Answers1

39

I did exactly the same thing. Here's how I made it work:

  1. Add the UIWebView as a subview of the UIScrollView (obviously ;-)
  2. Disable the native scrolling of the UIWebView (you can do that by iterating through the subviews of the UIWebView until you find it's UIScrollView and set scrollEnabled = NO on it.
  3. Set the contentSize of your UIScrollView and the UIWebView's frame to the size of the UIWebViews HTML content.

The last point is bit tricky because you cannot be sure that the HTML is completely rendered when webViewDidFinishLoad: gets called on the UIWebViewDelegate.

Here's a reliable way to get the HTML content size:

1.Add a javascript function to the HTML that gets called when the HTML Document is ready:

window.onload = function() {
    window.location.href = "ready://" + document.body.offsetHeight;
}

This functions sends a request that has the content height in it's URL.

2.In your UIWebViewDelegate you intercept this request:

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
        NSURL *url = [request URL];
        if (navigationType == UIWebViewNavigationTypeOther) {
           if ([[url scheme] isEqualToString:@"ready"]) {
               float contentHeight = [[url host] floatValue];
               yourScrollView.contentSize = CGSizeMake(yourScrollView.frame.size.width, contentHeight + yourWebView.frame.origin.y);
               CGRect fr = yourWebView.frame;
               fr.size = CGSizeMake(yourWebView.frame.size.width, contentHeight);
               yourWebView.frame = fr;

               return NO;       
        }

        return YES;
}

Hope that helps

UPDATE

Here is the Swift 2 version:

func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
    guard navigationType == .Other else { return true }
    if let url = request.URL, let host = url.host {
        guard url.scheme == "ready" else { return true }
        if let contentHeight = Float(host) {
            yourScrollView.contentSize = CGSizeMake(yourScrollView.bounds.size.width, CGFloat(contentHeight))
            var fr = webView.frame
            fr.size = CGSizeMake(fr.size.width, CGFloat(contentHeight))
            webView.frame = fr
        }

        return false
    }

    return true
}
joern
  • 27,354
  • 7
  • 90
  • 105
  • Can someone translate it to swift? – Yestay Muratov Jun 09 '15 at 08:17
  • @YestayMuratov Sorry for the late reply. I added a Swift 2 version to my answer – joern Oct 13 '15 at 08:51
  • @joern that will not work if HTML content has something like "Show More" link with JS attached to it (to expand or load and expand an article), right? – OgreSwamp Apr 08 '16 at 14:05
  • 1
    @OgreSwamp That's correct. It does not work with HTML content that changes its height after user interactions. This blogpost describes a way how to handle the case of a "Show more" scenario: http://www.pixeldock.com/blog/set-a-uiwebviews-height-to-the-height-of-its-html-content-using-auto-layout/ The example uses Auto Layout but you can use the KVO part also without using Auto Layout. TLDR; You observe the `contentSize` of the `UIScrollView` inside the `UIWebView` and change the height of the `UIWebView` whenever the height of the `contentView`changes. – joern Apr 08 '16 at 14:58