Saturday, September 1, 2012

10 tips for getting that native iOS feel with PhoneGap

At Outbox we have been hard at work building our iPad application with PhoneGap. I wanted to share some of the lessons my team has learned so far. All of the following tips have been tested on iOS 4, 5, and 6 with PhoneGap 1.8. Also note that these tips apply just as well to web applications running on mobile Safari.

Want more information about developing high quality PhoneGap applications, with in-depth explanations and working code examples? Check out phonegap-tips.com.

Tip #1 - Test on old hardware

Your app looks and feels great on that shiny new iPad 3 or iPhone 4S, right? Unfortunately, not everyone runs out to drop $500 whenever Apple releases a new toy. Get your hands on a first generation iPad or third generation iPhone. Always test on them first. If your app performs acceptably on on old hardware it is going to be blazing fast on the newer hardware.

Tip #2 - Use pre-emptive 3D transforms

If an element is going to be transformed in response to user interaction put an identity 3D transform on the element before any user interaction happens, for example:
-webkit-transform: translate3d(0px,0px,0px);
When the user first interacts with the element the initial movement will be much smoother.

Tip #3 - Show or hide elements with 3D transforms when possible

Invariably you will have some elements that need to be shown or hidden based on user interaction. For example, a modal dialog or menu of some sort. The easiest way to do this is usually display:none or visibility:hidden, however, you can achieve a significant speed increase by using a 3D transform to position the element offscreen. In my experience, using this technique to show or hide menus resulted in a 3-5X speed increase over display:none.

Tip #4 - Preload images

Even though your images are already stored on the iOS device there will still be a noticeable "pop-in" when the images are first displayed. I found CSS3 caching to be the best method for dealing with this issue. If you are not keen on enumerating and maintaining a list of every background image you use in your app, here is a jQuery plugin that will preload any background image referenced in your CSS.

Tip #5 - Phark image replacement does not play nice with 3D transforms

Image replacement by setting a large, negative text-indent is great for accessibility since screen readers will find the text and browsers will only display the image. However, the text-indent will make for unbearably choppy 3D transitions in mobile Safari (this is actually related to tip #7). Fortunately, there are alternatives to Phark that perform much better.

Tip #6 - Velocity scrolling!

Velocity scrolling is a super easy way to get a native feel in the scrollable portions of your app. It is simple to enable it on any scrollable area with a CSS rule:
-webkit-overflow-scrolling: touch;
That's it! However, be warned, Apple's current implementation is a little bit buggy.

Tip #7 - Respect maximum texture sizes

Each iOS device has a maximum texture size. If you try to smoothly transform an element larger than the maximum texture size, the element will be broken into smaller tiles before the transform is applied, and this a very slow and ugly process.

Tip #8 - Hide large, off-screen images

If your app displays many large images, hide the images that are off-screen. Otherwise, it doesn't take many large images to unceremoniously crash your app. Both display:none and visibility:hidden work just fine, but tip #3 will not cut it here.

Tip #9 - Eliminate tap event delays

It takes mobile Safari about a third of a second (300 milliseconds) to decide that a touch start event followed by a touch end event should be synthesized into a click event. Plenty of frameworks, such as jQuery Mobile, have "tap" handling code that can eliminate this delay for you.

Tip #10 - Disable tap highlighting

Have you ever noticed that translucent gray highlight mobile Safari puts on links and buttons when they are clicked? It is a dead giveaway that your app is not native, but you can get rid of it by making the highlight color completely transparent:
-webkit-tap-highlight-color:rgba(0,0,0,0);
Overall we're loving PhoneGap at Outbox. Hopefully these tips will help make a few more happy PhoneGappers!

Update
This post got picked up by Hacker News. See the discussion here.

35 comments:

  1. Very nice. I've made a few apps with phonegap, I found that mobile safari in a webview is much worst than the actual application.

    ReplyDelete
    Replies
    1. Thanks, I'm glad you liked it. Yes, it is true that UIWebView is not nearly as fast as all native. However, like most startups we have limited bandwidth, and I already knew how to build web apps, so this was an easy way to get started. We will also be launching a browser based web app in the near future, and heading down this path gives us a nice jump start that we would not have with an all native app.

      Delete
  2. Great Post! Thanks for the useful tips :)

    ReplyDelete
  3. Great tips.

    By the way, regarding scrolling, how are you handling pre iOS 5 devices? You do make a fallback to iScroll or something similar? How buggy has `-webkit-overflow-scrolling ` been on your app?


    Thanks!

    ReplyDelete
    Replies
    1. Right now we are not handling them at all. They just get the normal UIWebView scroll behavior, but we may implement a fallback at some point. Regarding bugs, the two major ones we dealt with were some strange interactions with the PhoneGap iframe (linked to Google groups discussion about this in the article) and problems with relatively positioned elements disappearing (also linkekd). Both had simple fixes. So fortunately there have been no showstoppers yet.

      Delete
  4. Coud I see a result application with those improvements?

    ReplyDelete
    Replies
    1. We are still pending approval in the App Store at the moment, but I will work on a follow-up post with some examples.

      Delete
  5. This is great! Answers a lot of questions I've had on my first PhoneGap/Cordova project. Any hints for button styles that feel like native iOS? I feel like _.delay()ing the button's click action in order to give time for the depress effect is somehow weird...

    ReplyDelete
    Replies
    1. Thanks William! I use the :active pseudo-class for the depressed button style and use the _.delay() trick you just mentioned to give the button a chance to become "unpressed" again. You're right, it does feel weird, but I haven't found anything better yet.

      Delete
    2. Hi! Thanks for the article. I also found this quite interesting: http://www.adobe.com/devnet/phonegap/articles/creating-apps-with-phonegap-lessons.html

      Any chance you could explain this _.delay() trick? Is this something only available with Underscore.js?
      I'm not sure in what case it would be used.

      Thanks!

      Delete
    3. The _.delay trick is equivalent to setTimeout(function() { ... }, 0), it is just a helper offered by underscore. So it effectively delays the execution of whatever function you provide until the current call stack has cleared.

      Delete
  6. Why did you choose PhoneGap over Titanium?

    ReplyDelete
    Replies
    1. I don't have firsthand experience with Titanium, so take this reply with a grain of salt, and please correct me if I am wrong. My understanding is that Titanium requires you code against their Javascript SDK which in turn makes native UI calls on mobile devices or generates HTML5 goodness on the web. As a result, you can get a "more native" experience with Titanium than you can with PhoneGap. With PhoneGap on the other hand, you just write a web app like you normally would and call into the native extensions as needed. Ultimately, I just felt more comfortable with the plain old web technologies PhoneGap allows you to use, so I went down that road.

      Delete
  7. Thank you very much for this enlightening post. I'm just at the start of a large project involving several apps that are gonna be build with phonegap. Your post is probably gonna save me lots-a time and headaches.

    ReplyDelete
  8. Great post Michael. I just want to point out that many of these tips are also very germain to Android PhoneGap developers as well.

    ReplyDelete
  9. I have (help) build various proffesional phonegap application, some of which had to specifically have a 'native' feel. To my surprise you were actually able to give some tips I hadn't thought of myself, but in general I only can say: absolutely right you are.

    Now, some footnotes and tips of my own:
    - webkit-overflow-scrolling I would advice against, as it isn't supported on versions of iOS and I am think it doesn't work on android either.
    - if you're using the phonegap filesystem or database, try doing all asynchronous actions in parallel (this can save a lot of time)
    - do not make the DOM to big (this will crash the app especially on older iOS devices and some android devices)
    - don't use css3 gradients if you can use images (images are far more efficient)
    - make sure your html is build *logically*, as this allows you to create entirely different UI's for android and iOS purely based on the css.

    Might be a lot more, but those are what I can think of right now.

    ReplyDelete
    Replies
    1. Thanks, more great tips! The gradient tip is particularly interesting, I'm going to have to try that one out myself...

      Delete
  10. My 2 cents here:
    -webkit-touch-callout

    to avoid the tap-and-hold selection stuff.

    =)

    ReplyDelete
    Replies
    1. wow that's a great tip... I've been using this little script for that thing! https://github.com/ubilabs/touchstates

      good thing to know I guess! Thanks a lot Everton

      Cheers

      Delete
    2. What's -webkit-touch-callout used for? Google returned no result :).

      Thanks,

      Delete
    3. Thanks Everton. This helped a lot.

      Delete
  11. totally liked the point " -webkit-overflow-scrolling: touch; "

    Thanks a lot.

    ReplyDelete
  12. Hi, great post! Could you provide more detail on item #9 - tap delays?
    - I am attaching jQuery 'click' events to my buttons and it seems there is a minor delay...

    ReplyDelete
  13. I plan to write an phonegap app that will interact with a Microsoft SQL Server. It will store articles on that server and on SQL lite in the phone with html5 if that's possible.
    I read on internet that it better to use a php server to send and retrieve data between phone and that SQL server. What do you think? What is your opinion about writing a complex data manipulation app. for android phones using phonegap and an online php web server for json data transfer between phone and microsoft sql server. What will you do if you were in my shoes? Thanks!

    ReplyDelete
    Replies
    1. I'd agree that you're probably better off with PHP (or some other server side technology) doing the database interactions, and having your app interact with that over AJAX. You'll gain a little flexibility in your storage technology (if SQL server doesn't cut it one day) and have more control over security, caching, etc.

      Delete
  14. Great post.

    Question - the css 3d transforms only work on Android 4? If so, we must use js animations on android 2 and 3?

    Many thanks
    Jeremy

    ReplyDelete
    Replies
    1. My experience with the tips above was with iOS, but it appears Android 3 and 4 should support 3d transforms (http://caniuse.com/transforms3d). Android hardware varies a little more than iOS hardware, so the extent to which these tips will have a positive impact on your application's performance may vary as well.

      Delete
  15. Hi, thanks for the suggestions. I have a question about point #10: what if I want to disable not just the highlight but also the click sound? I find that phonegap build triggers a lot of undesired click events which I'm trying to disable without success... I posted also a detailed questoion here.

    ReplyDelete
  16. Man am I happy to find this post.

    These tips took my handmade-menu to a new level in iPhone. Love U :)

    /Sweden

    ReplyDelete
  17. Hi Michael,

    Thanks for your useful tips, I'm not sure I'm clear about tip #3, did you mean using transition e.g. top: 1000px; left: 1000px; duration: 0s to hide an element instead of display: none?

    Thanks,

    ReplyDelete
  18. Hello! Have you actually tested tip number 8? Or do you know resources proving it? Also, if this is true, why isn't the UIWebview doing it automatically?

    ReplyDelete
  19. Nice tips. Native-feel isn't that difficult-to-achieve process if you've written your html and JS from scratch. You'd still need some libraries, but not the ones that take over your app design like JQTouch or Sencha.

    Things that you might consider when developing your PG app:

    - CSS animation isn't always the fastest and anti-flickering solution. To a couple of my apps it is perfect for light weight fixed/absolute div but not img elements.
    - For image/canvas elements, jQuery Transition plugin has a decent performance when hardware acceleration is there using translate3d(xxx).
    - For button-like DIVS, use ontouchstart to trigger if and only if the divs aren't to be scrolled, otherwise use ontouchend.
    - If you've got large CANVAS elements placed off-screen, they still use memory. Using display:none will disable Javascript called drawings on those elements. You can use visibility:hidden instead!
    - Figured out that tweaking for better performance isn't straightforward for all apps. Every PG app has different performance issues that are related to its own CSS and HTML. Spending a week or two to experiment and tweak would definitely give good results in terms of how native your PG app feels like.

    Thanks for the article, mate.

    Abdullah

    ReplyDelete