22

Summary of the problem: When browsing non-English sites that does not explicitly specify the correct character encoding with UIWebView on the iOS, the page cannot be displayed correctly.

Detailed explanation: As the loadRequest: method in UIWebView will use the encoding specified in the charset header sent from the web server or the charset written in the HTML meta tag, and default to iso-8859-1 (well, I am not too sure about this) when charset is not specified, which cause non-English sites cannot display properly.

I have tried to Google for a way to change the charset that the UIWebView use, but the only way is to use the loadData:MIMEType:textEncodingName:baseURL: method to specify the encoding name.

However, it is not a good idea to use loadData:MIMEType:textEncodingName:baseURL: + NSURLConnection instead of loadRequest:, because UIWebView won't call the delegate method webView:shouldStartLoadWithRequest:navigationType: for frames, and even if we found a way to get notified when UIWebView load a frame, we cannot call loadData:MIMEType:textEncodingName:baseURL: to load the frame content, because it will load the frame as the outer page if we do that.

Besides, I have tried to use a javascript hack, but seems that property is read-only and cannot be changed.

[webView stringByEvaluatingJavaScriptFromString:@"document.characterSet='utf-8';"];  

Another workaround is inserting a meta tag to the HTML, and ask UIWebView to load the modified code, but the frame problem mentioned above also apply here.

<meta http-equiv="Content-Type" content="text/html;charset=utf-8">

Question: Is there any better solution that can change the character encoding in a loaded webpage in UIWebView, and handle frames properly?

user454322
  • 7,300
  • 5
  • 41
  • 52
howanghk
  • 3,070
  • 2
  • 21
  • 34

2 Answers2

6

You can do this by manually loading the HTML, modifying it, and then loading the modified content into the UIWebView.

  • manually load the HTML from the page that doesn't include the meta tag, into a string (e.g. use NSURLConnection)
  • using string manipulation, insert your appropriate encoding meta tag into the manually loaded HTML
  • set the HTML in your web view to the modified string using loadHTMLString:

Now, this will work fine for a web page that contains no links. If it contains links, then you will find that after they click on a link, the new page will not have your modification in place. Therefore, you will need to manually intercept all of the clicks. Here's how you can do that:

Simon Woodside
  • 7,175
  • 5
  • 50
  • 66
  • Will you receive the delegate callback for frames as well? – Till Nov 24 '11 at 01:40
  • According to the documentation, yes. http://developer.apple.com/library/IOs/#documentation/UIKit/Reference/UIWebViewDelegate_Protocol/Reference/Reference.html – Simon Woodside Nov 25 '11 at 19:52
  • how did you "modify the content before setting it into the UIWebView" ? loadHTMLString cannot load into a specific frame but always the root document. – howanghk Sep 25 '12 at 04:42
1

This is what I do,
first find out if it is text, if it is, I get the data, set the encoding and load it using UIWebView>loadData:MIMEType:textEncodingName:baseURL.
Here is the code:

  1. Get the MIMEType sending a synchronous HEAD request.
    We want to use a HEAD HTTP request which generally is fast enough.
    And we want to make the request synchronously for two reasons, we need the request's result to continue, and we want to avoid concurrent request problems like this.

     
    NSMutableURLRequest *headRequest = [NSMutableURLRequest requestWithURL:url];
    [headRequest setHTTPMethod:@"HEAD"];
    NSHTTPURLResponse *headResponse;
    NSError *error = nil;
    [NSURLConnection sendSynchronousRequest:headRequest
                          returningResponse:&headResponse
                                      error:&error];
    if (error != nil) {
        NSLog(@"loadURLWithString %@",[error localizedDescription]);
    }
    NSString *mimeType = [headResponse MIMEType];
    

       


  2. Then if it is text, load it with UIWebView>loadData:MIMEType:textEncodingName:baseURL
    To maintain the application responsive, it is recommended to put the following code into a block and run it inside a GCD queue.

    
    if([mimeType rangeOfString:@"text"
                       options:NSCaseInsensitiveSearch].location == 0) {
    
       [wview loadData:[NSData dataWithContentsOfURL: url] MIMEType:mimeType
            textEncodingName:encoding baseURL:nil];
    
Community
  • 1
  • 1
user454322
  • 7,300
  • 5
  • 41
  • 52
  • Thanks for answering. However this will only work on the root document. If there are iframes or frameset it will not work. Please read the 4th paragraph for more detail why it didn't solve the problem. – howanghk Sep 05 '13 at 09:13
  • Besides, using synchronous request and -[NSString initWithContentsOfURL] are highly __discouraged__ as it would block your main thread, or you have to dispatch them into a background thread. Spawning thread is quite an expensive operations, and therefore asynchronous request is a better approach. – howanghk Sep 05 '13 at 09:15
  • Updated the answer. Haven't tried for documents with iframes or framesets but, this approach was useful for me and might be useful for some one else. As for the second comment, I am using GCD and blocks to avoid blocking the main thread or spawning threads. – user454322 Sep 05 '13 at 10:51
  • https://encoding.spec.whatwg.org/#names-and-labels. Useful link to know the possible encoding name options. – Conor May 08 '16 at 17:45