rss

How to overcome the iframe zoom limitation in iOS

Friday, March 15, 2013

The growing demand for fast prototyping of new mobile web applications is raising new challenges to mobile developers. An example, is the security limitation in iOS's Safari: iframes aren't allowed to hide content or zoom. Although the main idea is to protect the users from hidden malicious elements, it also restricts their actions as they cannot see the content with the zoom they want. This raises usability problems.

Nevertheless, there is the possibly to simulate this feature by hacking the CSS property zoom. This property controls the magnification level for the current element. The rendering effect for the element is that of a "zoom" function on a camera. The possible values are:

normal No magnification is applied. The object is rendered as it normally would be.
[number] Positive floating point number indicating a zoom factor. Numbers smaller than 1.0 indicate a “zoom out” or size reduction effect, while numbers greater than 1.0 indicate a magnifying effect.
[percentage] Positive floating point number, followed by a percentage character (“%”) which indicates a zoom factor. Percentages smaller than 100% indicate a “zoom out” or size reduction effect, while numbers greater than 100% indicate a magnifying effect.

You might want to take a look at the following CSS3 Transforms, if you want to create a cross-browser zooming mechanism:

zoom: 0.85;
-moz-transform: scale(0.85);
-moz-transform-origin: 0 0;
-o-transform: scale(0.85);
-o-transform-origin: 0 0;
-webkit-transform: scale(0.85);
-webkit-transform-origin: 0 0;

Now, we shall see an example:

div { zoom: 150% }
<div style="zoom: 150%">Hello!</div>

Of course, I would try to avoid the use of the zoom property to increase the font size. I would probably use font-size: 200%, as it is standard and supported.

The next step is to use Javascript to dynamically modify these properties. Ideally, we want to use pinch zoom gesture. The easiest solution is to use TouchSwipe, a jQuery plugin for touch and gesture-based interaction.

First, we define a JavaScript function that updates the CSS zoom property. In this case, I set the zoom to all iframe elements. However you can do it for a specific element as well.

function updateZoomProperty( pinchZoom )
{
  $("iframe").css("zoom", pinchZoom);
  $("iframe").css("-webkit-transform", "scale(" + pinchZoom + ")");
  $("iframe").css("-webkit-transform-origin", "0 0");
  $("iframe").css("-moz-transform", "scale(" + pinchZoom + ")");
  $("iframe").css("-moz-transform-origin", "0 0");
  $("iframe").css("-o-transform", "scale(" + pinchZoom + ")");
  $("iframe").css("-o-transform-origin", "0 0"); 
}

Then, we define the function that recognizes pinch zoom gesture, using TouchSwipe.

function activateSwipe(itemId, touchItemId)
{
  $('#' + itemId).contents().find(touchItemId).swipe( {
    pinchIn:function(event, direction, distance, 
                     duration, fingerCount, pinchZoom)
    {
       updateZoomProperty(pinchZoom);
    },
    pinchOut:function(event, direction, distance, 
                      duration, fingerCount, pinchZoom)
    {
       updateZoomProperty(pinchZoom);
    },
    pinchStatus:function(event, phase, direction, 
                         distance, duration, fingerCount, 
                         pinchZoom) 
    {
       console.log(
             "Pinch zoom scale " + pinchZoom +
             " <br/>Distance pinched " + distance +
             " <br/>Direction " + direction
 );
    },
    fingers:2,
    pinchThreshold:0
  });
}

Now, we can define a function to dynamically inject an iframe.

function injectFrameElement( id, itemId, url )
{
  var h = $(document).height();
   $(itemId).html(
      '<iframe id="' + id + '" width="100%" height="' + h + 'px" ' +
        'frameborder="0" marginheight="0" marginwidth="0" src="' + url + '" ' +
        ' onload="activateSwipe(\'' + id + '\',\'body\')">' + 
          '<p>Your browser does not support tampering 
               with iframe content.</p>' +
        '</iframe>'+
        '<small>Loading page ...</small>'
   );
}

And voilà! We only have to use the function in the following way.

injectFrameElement('frameviewer', '.myframe');
In this case, it will add the iframe to the following HTML element:
  <div class="myframe"></div>
This is just a draft idea. Please let me know your opinion.

5 comments:


Unknown said...

Very interesting. Do you have any suggestions on how I could prevent an iframe from being zoomed while the viewport behind it remains zoomable?



Bruno Simões said...

Dear reader,

Can you elaborate better your question because by default iOS devices do not allow zooming, e.g. you can only scroll through your iframe. Hence, by default it does what you need.

Best Regards,
B



Anonymous said...

I relish, lead to I discovered exactly what I was looking for.
You have ended my four day lengthy hunt! God Bless you man.

Have a great day. Bye

My web blog - wordpress theme developer



Anonymous said...

I really love your site.. Great colors & theme.
Did you make this site yourself? Please reply back as I’m
trying to create my very own site and would love to learn where you got this from or what the theme is named.

Thanks!

My web blog jogos de fazenda



Patrick Moore said...

Thank you very much for this solution! I have spent days scouring articles, blog entries and Stack Overflow discussions about how to implement zoom-able objects within non-user-scalable page. This looks like it will be a fine, cross-device solution. Thank you for your efforts, and I will reach out after testing & implementing.