iOS 6: Pull to Refresh (UIRefreshControl)

We’ve all seen them in apps: a screen is refreshed by pulling down on the view. 

screenshot

A little wheel starts spinning at the top, until the refresh has completed.  At that time, the wheel disappears, and the view bounces back into place.  Many of us have built our own custom versions of this functionality, however, as of iOS 6, things have gotten quite a bit easier. 

Introducing the UIRefreshControl, which is now a part of CocoaTouch in the iOS 6 SDK.  In order to use this with a Table View (right now, this control is only available for tables) follow these steps:

  1. Create a callback method to handle your refresh logic.
  2. Instantiate the UIRefreshControl with a basic “alloc/init”
  3. Connect an action to the refresh control to invoke your callback method when the ValueChange event is fired.
  4. Add the refresh control to the Table View Controller’s “refreshControl” property.

 

Step 1:

Create the “callback” method that in invoked when a user pulls down on the Table View.  The signature of the method should take one parameter: a pointer to the UIRefreshControl.  This control object has a couple of methods available to show (beginRefreshing) and hide (endRefreshing) itself.   Since this callback method won’t be invoked until the control has already started running, there is no need to call beginRefreshing here.

   1: -(void)refreshView:(UIRefreshControl *)refresh {
   2:     refresh.attributedTitle = [[NSAttributedString alloc] initWithString:@"Refreshing data..."];
   3:     
   4:     // custom refresh logic would be placed here...
   5:  
   6:     NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
   7:     [formatter setDateFormat:@"MMM d, h:mm a"];
   8:     NSString *lastUpdated = [NSString stringWithFormat:@"Last updated on %@", 
   9:                                    [formatter stringFromDate:[NSDate date]]];
  10:     refresh.attributedTitle = [[NSAttributedString alloc] initWithString:lastUpdated];
  11:     [refresh endRefreshing];
  12: }

Line 2 changes the text below the refresh icon to  “Refreshing data…”   You’ll notice that you can’t simply use a basic NSString with this control.  Instead, you’ll create an NSAttributedString (a String that allows you to define characteristics, such as a font, for a range of its characters).   After that, your custom refresh logic is executed (such as hitting a RESTful web service for the latest data).

Lines 6 – 10 are executed after the data has been refreshed.  In this case, an informational message is displayed below the refresh icon that reminds the user when the last update occurred.

Line 11 stops and hides the spinning refresh icon, and automatically pulls the Table View back into place.

 

Steps 2 – 4:

   1: // Inside a Table View Controller's viewDidLoad method
   2: UIRefreshControl *refresh = [[UIRefreshControl alloc] init];
   3: refresh.attributedTitle = [[NSAttributedString alloc] initWithString:@"Pull to Refresh"];
   4: [refresh addTarget:self
   5:          action:@selector(refreshView:)
   6:          forControlEvents:UIControlEventValueChanged];
   7: self.refreshControl = refresh;

Line 2 is self explanatory… since UIRefreshControl is part of UIKit in CocoaTouch, you’ll already have the necessary framework imported.

On line 3, you’ll set the initial message to the instructional “Pull to Refresh.”

Lines 4 – 6 show how to link your callback method to the “Value Change” event (which occurs when the user pulls down on the table view).  Most people are used to hooking up IBActions for controls… this is the programmatic way to do it.

Finally, line 7 assigns the UIRefeshControl to the Table View Controller’s “refreshControl” property (note that this is assigned to the View Controller, not the Table View).

 

Running the Code:

The initial view shows your table…

initialState

When the user pulls down on the table, they see the refresh control with the initial instructional message…

initialPullMsg

As the user continues to pull the table down, the icon begins to stretch…

stretchMsg

Once they pull down far enough, the table view snaps back a bit, the refresh icon changes to a spinning gear, and the active “Refreshing data…” message is displayed…

activeMsg 

Finally, once it’s finished, the refresh control disappears and the table snaps back into place.  The next time the user pulls down on the table, they’ll see an informational “Last updated on” message…

newPullMsg

 

Interface Builder:

You may be wondering, “isn’t there a way to do this in InterfaceBuilder?”  The answer is: yes, in part.  You won’t find the control in the Object Library.  However, you can add the refresh by selecting the Table View Controller and configuring it in the Attributes Inspector.  Setting the “Refreshing” option to “Enabled,” takes care of instantiating the UIRefreshControl with an attributed title, and assigns it to the Table View Controller’s “refreshControl” property.  So if you want to access the control programmatically, you do not need to create an IBOutlet… just use that property.

attributes-inspector

You won’t see any visual indication in the Table View Controller’s scene that it has been added, however if you look at the View Controller Hierarchy, it does list a “Refresh Control.”

refresh

Now, you would naturally assume that you can do a secondary click on this Refresh Control and drag from the Value Changed event to your View Controller’s header file (thus auto-generating an IBAction skeleton)…  and while it appears you can…

ibaction

…the action never fires.  Instead of creating an IBAction, you’ll need to programmatically assign the action to your callback method.   Use the refreshControl property for this purpose:

   1: // Inside a Table View Controller's viewDidLoad method
   2: [self.refreshControl addTarget:self
   3:                      action:@selector(refreshView:)
   4:                      forControlEvents:UIControlEventValueChanged];

iOS 6 as a GA release is only a few weeks old, so perhaps we’ll see this fixed and/or a visual control added to the Object Library in Interface Builder soon!

  • Amr Hossam

    Thank you very much for the easy and efficeint tutorial. I will give it a try today. Much appreciated.

  • Min

    I added the UIRefreshControl to my table view and it's working as expected, except that I have to pull down pretty far (almost 80% of the iphone 4s screen full height) to trigger refresh. Do you know how the control figures out how far you have to drag? is that a customizable property?

    thanks!

  • J Shapiro

    There isn't a property that lets you configure the length of the "pull" to trigger the refresh. The normal amount of pull with a simple table view is less that 45% (and that's all I've ever seen). I'd suggest posting a question at StackOverflow.com to see if anyone else has run into this issue.

  • Jaffy

    Thanks! This worked first time!

  • Rick Caldwell

    Thanks for the wonderful tutorial. I was able to port the Objective-C code example over to Xamarin.iOS and Parse. It works flawlessly.

  • Aaron

    great tutorial! Thanks

  • Vani

    can anyone guide me,how can we add refresh control in bottom of our collection view?
    Please guide

{Offer-Title}

{Offer-PageContent}
Click Here