55

I am making use of the UIWebView to render some HTML. However, although the width of my webview is 320 my HTML is still shown full width and can be scrolled horizontally.

I want to achieve the same thing the native mail application achieves which is it fits all content within that width without zooming out - how does the native mail application render HTML like this?

Update

I thought making use of the viewport meta tag will help, but I couldn't get this to work.

This is what is happening:

enter image description here

As you can see the content does not fit the device width. I've tried so many combinations of viewport meta tag. The below is an example of what happens when I try Martins suggestion.

Original HTML is can be found here.

The way this HTML is rendered by the native mail application is like so.

Abs
  • 56,052
  • 101
  • 275
  • 409
  • fill content using div tag with minimum width and height as well as minimum and maximum width and it will adjust accordingly to parent content – Paresh Navadiya May 19 '12 at 15:42
  • @safecase This isn't a HTML solution. I need to do this using objective-c or the iPhone storyboard or uiwebview config. – Abs May 19 '12 at 15:43
  • is your webpage specific to iphone, or does it share mobile and non-mobile users ? – Martin May 22 '12 at 12:37
  • see my edits, its just a viewport problem, I think. – Martin May 22 '12 at 13:02
  • capture the html content and add a width=320px to the html body tag before displaying the page. I had a similar problem and this was the only way i manage to get the problem solved. – alinoz May 22 '12 at 14:08
  • @alinoz - did you do anything else? I add that width in the style attribute and it did make things fit in the device screen but everything zoomed out. I dont' want the zoom out look to be honest. – Abs May 22 '12 at 14:23
  • @Abs - i have tried the "viewport" and I have played with the settings of the webview but didn't help. :( – alinoz May 23 '12 at 07:18

8 Answers8

133

Here's what you do:

In your UI controller that owns the web view, make it a UIWebViewDelegate. Then where you set the URL to load, set the delegate as the controller:

NSString *urlAddress = @"http://dl.dropbox.com/u/50941418/2-build.html";
NSURL *url = [NSURL URLWithString:urlAddress];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[webView loadRequest:requestObj];  
webView.delegate = self;

And finally implement the webViewDidFinishLoad: to correctly set the zoom level:

This option will applicable from iOS 5.0 and >

- (void)webViewDidFinishLoad:(UIWebView *)theWebView
{
  CGSize contentSize = theWebView.scrollView.contentSize;
  CGSize viewSize = theWebView.bounds.size;

  float rw = viewSize.width / contentSize.width;

  theWebView.scrollView.minimumZoomScale = rw;
  theWebView.scrollView.maximumZoomScale = rw;
  theWebView.scrollView.zoomScale = rw;  
}

Option B, you can try to alter the HTML (this example does the job but is less than perfect from an HTML parsing standpoint. I just wanted to illustrate my point. It does work for your example, and probably most cases. The inset of 40 can probably be detected programmatically, I didn't try to research that.

NSString *urlAddress = @"http://dl.dropbox.com/u/50941418/2-build.html";
NSURL *url = [NSURL URLWithString:urlAddress];

NSString *html = [NSString stringWithContentsOfURL:url encoding:[NSString defaultCStringEncoding] error:nil];
NSRange range = [html rangeOfString:@"<body"];

if(range.location != NSNotFound) {
  // Adjust style for mobile
  float inset = 40;
  NSString *style = [NSString stringWithFormat:@"<style>div {max-width: %fpx;}</style>", self.view.bounds.size.width - inset];
  html = [NSString stringWithFormat:@"%@%@%@", [html substringToIndex:range.location], style, [html substringFromIndex:range.location]];
}

[webView loadHTMLString:html baseURL:url];
starball
  • 20,030
  • 7
  • 43
  • 238
mprivat
  • 21,582
  • 4
  • 54
  • 64
  • Thank you very much for your answer. So I just tried this and it is better. However, what happens is that the content fits in the width of the device screen. But the text is small. The native mail application somehow scales the text and other elements whilst keeping the fixed width of the device. Any ideas on this? This is how the native Mail Application displays the same email: http://dl.dropbox.com/u/50941418/iphone_ex.PNG – Abs May 22 '12 at 14:03
  • I see what you mean. This can unfortunately only be done by messing with the HTML. You can see from your screenshot that it's not done at the graphics level because it's not all proportional. That's because the mail app probably alters the style. I've added option B to my answer to see how you can do that (don't forget to remove the delegate for this option or else it'll also try to scale). – mprivat May 22 '12 at 14:36
  • @mprivate yes that seems to work! Can you tell me more about the logic. Are you adding a new style block that sets the max-width for all divs? If so, if there is HTML with a pure table layout will this still work? Or have I misunderstood? – Abs May 24 '12 at 15:23
  • Yes it's simply setting an upper limit to the divs. I suspect that same style attribute could be applied to the HTML body tag. I haven't tried to be honest but I remember reading that you can. I'm guessing you will need to make some adjustments to the styles to support all types of HTML layouts but I'm sure you get the basic idea. – mprivat May 24 '12 at 15:35
  • Do you think setting `*{ max-width: %fpx; }` is a good idea? Also is the inset basically the padding to give to the HTML where the edge is the device screen? – Abs May 24 '12 at 15:41
  • I tried this on another HTML and it doesn't work as well. How can I improve it for this HTML: http://gallery.campaignmonitor.com/ViewEmail/r/204A1EB681D250A3/ – Abs May 24 '12 at 15:50
  • This is probably a whole new question of its own. The Mail app probably does some complicated HTML parsing and rewriting to achieve what it does. I think you might want to get help from the HTML designer community for explanations on how to rewrite the HTML. I was just trying to show you how the Mail app achieves the transformation using UIWebView – mprivat May 24 '12 at 17:55
  • I just tried that page you mentioned above and switching the style adjustment to using `*{ max-width: %fpx; }` and it worked perfectly from what I can tell. – mprivat May 25 '12 at 12:07
  • Really, I can't get it to work. When you say "don't forget to remove the delegate" what do you mean? – Abs May 25 '12 at 13:22
  • I meant don't do this: `webView.delegate = self;` if you are altering the HTML. Shoot me an email (in my profile), I'll send you my test project if you want. – mprivat May 25 '12 at 13:35
  • That would be great! :) Couldn't find your email on SO profile. But I put mine on my profile. Thanks. – Abs May 25 '12 at 13:44
  • @mprivat I know this is a little late but just stumbled across this question/answer. Any chance I could get a copy of your test project? – thiesdiggity Oct 27 '13 at 04:01
  • @thiesdiggity you can get it from my Dropbox. But I haven't opened in in over a year so the warranty is expired :) https://www.dropbox.com/s/4vqnw4veegxs6or/testwebview.zip – mprivat Oct 28 '13 at 15:25
  • 1
    it works, as I unchecked value for "Scales Page To Fit" Thank you – kalpesh jetani Aug 19 '15 at 06:53
  • this not work for me. InHTML contenteditable mode when begin edit, the webview automatic zoom out. – Trung Phan Jun 22 '16 at 06:48
57

Just add this:

webView.scalesPageToFit = YES;
Denis
  • 3,167
  • 1
  • 22
  • 23
  • 3
    this worked much better for me, it didn't have the issue where the page height was way too large like the accepted answer does. – Rocky Pulley Mar 18 '13 at 14:41
  • 1
    Just found this and it saved me from hours of frustration. Dont know why it hasnt got more upvotes. – Alex Aug 18 '13 at 06:14
  • 1
    This works well if your content is formatted correctly. However if you want to shrink the content you have to use the selected answer and modify it a bit (subtract a small amount from the viewSize's width or height depending on what you want to shrink. That's why this doesn't have more up votes. – Matt Hudson Mar 09 '14 at 04:42
  • This worked for me, whereas the accepted solution above launched mobile safari and I needed the WebView to stay within the app but overlaid. Simple! – Nate_Hirsch Aug 27 '14 at 17:51
  • In my case, this approach shrink the original html page, which makes the site pretty unreadable if using the same css file. @Martin's solution works better for my case. – Yuming Cao Dec 31 '14 at 06:16
6

Typically, you should use the viewport meta tag. But its use is very erratic, mostly if you want a cross platform web page.

It also depends of what content and css you have.

For my iPhone homepage, which must auto-resize from portrait to lanscape, I use this :

<meta name="viewport" content="width=device-width; minimum-scale=1.0; maximum-scale=1.0; user-scalable=no">

If you need special resize, you can also use the event :

<body onorientationchange="updateOrientation();">

with the corresponding funciton in your javascript :

function updateOrientation() {
  if(Math.abs(window.orientation)==90)
      // landscape
  else
      // portrait   
}

EDIT :

Seeing you page source, it seems you made it with a web editor, don't you ?

Ok, I understand. Your main div has a width of 600px. The iphone screen resolution is 320x480. 600 > 320 so it exceeds the screen bounds.

Now, let's make some simple operations:

320 / 600 = 0.53
480 / 600 = 0.8

So you want to zoom out 0.5 times minimum and 0.8 times maximum. Lets change the viewport :

<meta name="viewport" content="width=device-width; minimum-scale=0.5; maximum-scale=0.8; user-scalable=no"> 
handet87
  • 549
  • 5
  • 22
Martin
  • 11,881
  • 6
  • 64
  • 110
  • thank you for your answer. But I tried the above meta tag and it didn't work. See my question update, – Abs May 22 '12 at 11:00
  • 1
    I tried the new viewport meta tag and it seems to achieve exactly what mprivat answer does. However, there is a still a problem. See my comment to mprivat's answer. In addition, this app is supposed to take in different HTML and render it the same way that the native mail application does so I'm not sure that calculation will work for all HTML emails. – Abs May 22 '12 at 14:08
  • What about using Javascript to make your text bigger? When you page has finished loading call you can use: `- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script` webview method – Martin May 22 '12 at 15:51
5

What worked for me was to select the UIWebView in Interface Builder and check the box that says "Scales Page To Fit":

enter image description here

Chris Livdahl
  • 4,662
  • 2
  • 38
  • 33
1
@implementation UIWebView (Resize)

- (void)sizeViewPortToFitWidth {
    [self stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"document.querySelector('meta[name=\"viewport\"]').setAttribute('content', 'width=%d;', false); ", (int)self.frame.size.width]];
}

@end
Werner Altewischer
  • 10,080
  • 4
  • 53
  • 60
  • The querySelector needs escaped quotes around viewport to be valid javascript: `document.querySelector('meta[name=\"viewport\"]')` – Mike Richards Jul 03 '17 at 14:48
1

Swift 3:

Use this extension to resize contents of webview according to size of a webview.

extension UIWebView {
    ///Method to fit content of webview inside webview according to different screen size
    func resizeWebContent() {
        let contentSize = self.scrollView.contentSize
        let viewSize = self.bounds.size
        let zoomScale = viewSize.width/contentSize.width
        self.scrollView.minimumZoomScale = zoomScale
        self.scrollView.maximumZoomScale = zoomScale
        self.scrollView.zoomScale = zoomScale
    }
}

How to invoke?

webViewOutlet.resizeWebContent()
aashish tamsya
  • 4,903
  • 3
  • 23
  • 34
0

You may generate an NSAttributedString from HTML (do it on background):

@implementation NSAttributedString (Utils)

+ (void)parseHTML:(NSString *)html withCompletion:(void (^)(NSAttributedString *completion, NSError *error))completion
{
    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
        NSError * __autoreleasing *error = nil;
        NSAttributedString *result = [[NSAttributedString alloc] initWithData:[html dataUsingEncoding:NSUTF8StringEncoding]
                                                                      options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
                                                                                NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}
                                                           documentAttributes:nil error:error];
        NSError *safeError = (error != nil) ? *error : nil;
        dispatch_sync(dispatch_get_main_queue(), ^(void){
            completion(result, safeError);
        });
    });
}

@end

And show it through UITextView instance:

[NSAttributedString parseHTML:htmlString withCompletion:^(NSAttributedString *parseResult, NSError *error) {
        bodyTextView.attributedText = parseResult;
}];

Some layout features, though, may corrupt with this approach.

nrx
  • 350
  • 4
  • 10
0

i solved the issue by unticking "scales page to fit" enter image description here

Omar N Shamali
  • 503
  • 5
  • 11