KISS

Be Among the 20% of the Best!

iOS: Scaling Web Content in UIWebView

| comments

Hello there!

The iOS application I’m developing now has its UI in the form of a web application (HTML5 + JavaScript + CSS) displayed inside a UIWebView, a widget that allows applications to render web content. Because the native web app’s size is constant and less than the dimensions of an iPad screen, the native application supports both fullscreen and non-fullscreen modes. The question is how to scale the web content properly?

Method 1 (CSS scaling)

Make the webView fill the entire window of the application and support scaling from on the web side, with something like the -webkit-transform: scale(1.2); CSS attribute applied to the main wrapper. However, this complicates the web code and requires it to know and monitor the dimensions of the webView. So, skip it for now.

Method 2 (using UIWebView’s scalesPageToFit property)

This is the most common answer to the question of scaling. To use it:

  • index.html should have <meta name="viewport" content="width=600,user-scalable=no" /> tag in the <head>, where 600 is the nominal width of the web page.

  • self.webView.scalesPageToFit = YES; — enables the webView’s scaling.

  • The following code alters the webView’s frame every time the fullscreen mode changes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- (void)updateSubviewsFramesWhenChangingFullscreen:(BOOL)isFullscreen {
    // for some reason, the view has portrait's frame in landscape orientation
    // after reopening the app. so we set width to the bigger value
    CGRect frame = self.view.frame;
    CGSize correctSize;
    correctSize.width = MAX(frame.size.width, frame.size.height);
    correctSize.height = MIN(frame.size.width, frame.size.height);
    frame.size = correctSize;

    CGSize webAppSize = CGSizeMake(kWebAppWidth, kWebAppHeight);
    CGRect newFrame = self.webView.frame;

    if (isFullscreen) {
        float newWidth = floorf(frame.size.width / 10) * 10;
        // avoid fractional coordinates here
        float scaleFactor = newWidth / webAppSize.width;
        float newHeight = webAppSize.height * scaleFactor;
        float xShift = (frame.size.width - newWidth) / 2;
        float yShift = (frame.size.height - newHeight) / 2;
        newFrame = CGRectMake(xShift, yShift, frame.size.width - (xShift * 2), frame.size.height - (yShift * 2));
    } else {
        newFrame = [self frameOfViewWithSize:webAppSize centeredInViewWithFrame:frame];
    }
    self.webView.frame = newFrame;
}

This method seemed to be working well, until someone has noticed that the graphics (borders or something) was not rendered properly on retina screens. Interfering lines would appear out of blue. Thus, we had to try the next solution.

Method 3 (scaling the webView using its transform matrix)

Here I employed the transform property, that is available in any UIView object, to apply the correct affine scale matrix. The steps:

  • index.html doesn’t have <meta name="viewport" content="width=600,user-scalable=no" /> tag.

  • Remove self.webView.scalesPageToFit = YES;.

  • The resizing code looks like this now:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (void)updateSubviewsFramesWhenChangingFullscreen:(BOOL)isFullscreen {
    self.webView.bounds = CGRectMake(0, 0, kWebAppWidth, kWebAppHeight);
    if (isFullscreen) {
        CGRect frame = self.view.frame;
        // for some reason, the view has portrait's frame in landscape orientation
        // after reopening the app. so we set width to the bigger value
        CGSize correctSize;
        correctSize.width = MAX(frame.size.width, frame.size.height);
        correctSize.height = MIN(frame.size.width, frame.size.height);
        frame.size = correctSize;

        float scaleFactor = frame.size.width / kWebAppWidth;
        self.webView.transform = CGAffineTransformMakeScale(scaleFactor, scaleFactor);
    } else {
        CGAffineTransform transform = CGAffineTransformIdentity;
        self.webView.transform = transform;
    }
}

It’s interesting that this method produces better scaled graphics rendering. I suppose this happens because here the iOS takes the original rendered view and scales it as is, whereas in the previous solution UIWebView could look into the page markup and zoom different elements in various ways.

Hope this information will help someone else. Stay tuned!

Comments