Question

I have a UIWebView which displays some local text and images. Either I've made a mistake somewhere, or the UIWebView doesn't automatically handle the @2x suffix for high resolution images.

So, my question is has anyone else successfully loaded @2x images into a UIWebView through the normal means, or do I need to do something special? Can I somehow detect if my user has a retina display?

Was it helpful?

Solution

As per your request...

Test for functionality. If you want to know if you need to display a retina image or a regular image, then test if your device has a retina display, not that it's of a particular model (future proof your application as best you can, means you have to change less stuff when a new model comes out). To do this, you can use the following sample code:

if([[UIScreen mainScreen] respondsToSelector:@selector(scale)] &&
   [[UIScreen mainScreen] scale] == 2.0)
{
    /* We have a retina display. */
}
else
{
    /* We do not. */
}

This code is safe, as of the time I wrote this, on all models and on all firmware versions. It will be safe on future versions as well until Apple deprecates the scale method, which may never happen.

More on your question, I don't know how to do that in a webview without having the locations to a retina image and a non-retina image beforehand. Once I have that info, I have (in the past) used it to replace some known sentinel text that the web page expected me to replace, as in I would put something in the HTML that had say: {{{image_location}}} where I could download the HTML data, get it into string format, then do a replace on that string replacing that text, with the URL of the @2x image if we're on a retina display, with the appropriate scale factor, or the normal sized image if not (using the above code).

Hope this helps if nobody comes along with a better solution.

OTHER TIPS

I use this Javascript hack to load scaled images when running on a retina device. It will change the src and width attribute on all images to use the corresponding scaled image. Note that I have only tested this with local images.

Setting display: none and then reset it on image load seams to fix some flickering. Also note that this code is probably not compatible with other browsers than WebKit.

document.addEventListener("DOMContentLoaded", function() {
  var scale = window.devicePixelRatio;
  // might want this to be scale != 2
  if (scale == undefined || scale == 1) {
    return;
  }

  var re = /^(.*)(\..*)$/;
  var images = document.getElementsByTagName("img");
  for (var i =  0; i < images.length; i++) {
    var img = images[i];
    var groups = re.exec(img.src);
    if (groups == null) {
      continue;
    }

    img.style.setProperty("display", "none");
    img.addEventListener("load", function(event) {
      event.target.width /= scale;
      event.target.style.setProperty("display", "");
    });
    img.src = groups[1] + "@" + scale + "x" + groups[2];
  }
});

Tips is to add this to a file called e.g. scaledimages.js and then use

<script type="text/javascript" src="scaledimages.js"></script>

Make sure the js-file is listed in "Copy Bundle Resources" and not in "Compiled Sources" in your targets "Build phases". Default Xcode seams detect Javascript files as something it likes to compile. Also note that the current script might break things if devicePixelRatio happen to be 3 or greater in the future, a precaution might be to change the (scale == undefined || scale == 1) to scale != 2 only load @2x for now.

Update: There is also a jQuery-Retina-Display-Plugin that does something similar but requires you to set the width attribute and seams to also use a HEAD request to see if the image exist, not sure how that will work for local files.

I supposed this can also be achieved by using some CSS or Javascript magic to display only the appropriate picture. The main idea is to insert the two pictures, normal and high resolution and then to set the CSS attribute display to none for the pictures to mask :

HTML file

<link href="default.css" rel="stylesheet" media="screen" type="text/css" />
<link href="highresolution.css" media="screen and (-webkit-min-device-pixel-ratio:2)" type="text/css" rel="stylesheet" />

...

<img src="image.png"    width="..."   height="..."    class="normalRes" />    
<img src="image@2x.png" width="..."   height="..."    class="highRes" />

CSS file : default.css

.normalRes { display:block } /* or anything else */
.highRes   { display:none  }

CSS file : highresolution.css

.normalRes { display:none  }
.highRes   { display:block }  /* or anything else */

I've test it a little bit and it worked well. Need more tests now. I've found the solution here : http://www.mobilexweb.com/blog/iphone4-ios4-detection-safari-viewport

UPDATE 2012-06-24

Here is another solution with no need for highresolution.css file.
Add the following code in default.css.

@media all and (-webkit-min-device-pixel-ratio: 2) {

   .normalRes { display:none  }
   .highRes   { display:block }

   ...

Just always do:

<img src="image@2x.png" width={half-width} />

for CSS referenced background images, use -webkit-background-size to half-size them.

Disadvantage: Non-retina devices will download oversized images.

The best solution is to use the high-res images for both retina and non-retina based WebViews. Then set the size of the image (in points) using the width and height attribute.

<img src="hi-res-icon.png" width="10px" height="10px" />

As for the detection, you can use this code sample. I’ve just updated it to also detect iPhone 4.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top