iOS Pull-to-refresh in Mobile Safari With Native Scrolling

| Comments

The iOS6 developer kit for native apps is out there for a while and Apple finally added a out of the box pull-to-refresh control called UIRefreshControl.

However this feature is nothing new and used by plenty of apps in different implementations for years now. There is even a implementation for web applications included into iScroll which allows to simply make a block element scrollable and add the pull-to-refresh feature. Though this solution depends on a Javascript implementation to emulate native scrolling. Indeed this made sense until iOS 5 where Apple introduced the -webkit-overflow-scrolling css attribute to enable scrolling for overflowing block elements.

I strongly recommend the use of this attribute instead of Javascript scrolling! It seems awkward that iOS5 is out there for over a year and I still couldn’t find a pull-to-refresh implementation for mobile safari. So I decided to publish mine.

The concept to achieve that is pretty simple. The markup will be created out of two level nested block elements.

The outer div (red border) is scrollable and gets the -webkit-overflow-scrolling: touch; and attribute overflow-y: auto;. Then the content which needs to scrollable will be placed into the box (black border). The panel with the pull-to-refresh information will be absolute positioned behind the content div. This results into:

<div class="scrollable">
    <div class="pull-to-refresh">

    </div>
    <div class="wrap">
        <!-- Content here -->
    </div>
</div>

The background color between the pull-to-refresh panel and the content may be different and can be applied to the nested divs. Take care that the pull-to-refresh panel has the same background color as the surrounding div otherwise the pulling would look weird. Furthermore we will have to stretch the height of the content to 100% height of the surrounding div +1 pixel padding otherwise scrolling wouldn’t be possible if the inner content’s height is not taller than the div.scrollable height. Another downfall of native scrolling is that nested content might be scrambled while and after scrolling so we need to activate hardware acceleration for the div.wrap.

<style>
    .scrollable {
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
        position: relative;
        height: 100%;
        background: #fff;
    }

    .wrap {
        min-height: 100%;
        padding-bottom: 1px;
        background: #f5f5f5;
        -webkit-transform: translateZ(0);
    }

    .pull-to-refresh {
        height: 40px;
        width: 100%; 
        
        position: absolute; 
        left: 0; 
        top: -40px;

        z-index: -1;
    }
</style>

Now you should already be able to see the pull-to-refresh panel behind the content while scrolling with the rubber band. Now comes the Javascript. Using the scrollTop value of div.scrollable you should receive a negative value when the rubberband shows the pull-to-refresh panel. After the user releases the finger and the scrollTop value is below the height of the panel then the div.pull-to-refresh should receive position:static and start the loading!

To avoid bothering you with more details I build a jQuery plugin which easily applies the demo features to the markup. You just need to pass a callback function which returns a promise. Here an example.

<script>
$('.scrollable').pullToRefresh({
    callback: function() {
        var def = $.Deferred();
        
        setTimeout(function() {
            def.resolve();      
        }, 3000); 

        return def.promise();
    }
});
</script>

Works on iOS5 and 6. Download the project from my github repo!

Copyright © 2014 - Damien Antipa. Powered by Octopress