We’ve all seen them in apps: a screen is refreshed by pulling down on the view.
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:
- Create a callback method to handle your refresh logic.
- Instantiate the UIRefreshControl with a basic “alloc/init”
- Connect an action to the refresh control to invoke your callback method when the ValueChange event is fired.
- 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…
When the user pulls down on the table, they see the refresh control with the initial instructional message…
As the user continues to pull the table down, the icon begins to stretch…
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…
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…
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.
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.”
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…
…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!


Comments (8)
Amr Hossam -
October 22, 2012 at 9:03 am
Thank you very much for the easy and efficeint tutorial. I will give it a try today. Much appreciated.
Min -
November 3, 2012 at 2:29 am
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 -
November 3, 2012 at 7:43 am
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.
Matt -
April 21, 2013 at 3:01 pm
Maybe i missed something, but is the code provided the only implementation needed? I copied and pasted everything and it built with no problems. Then nothing happens when I try to pull down to refresh.
J Shapiro -
April 21, 2013 at 3:28 pm
Matt: As long as you are applying this refresh control to a table view controller, then you should see the control appear when you pull down on the table view. If you are seeing the refresh control, but the actual content in the table view isn't updating, remember that you need to replace the comment in line 4 with your actual refresh logic (whatever that might be).
Matt -
April 21, 2013 at 3:45 pm
Thanks J Shapiro.
My tableView is inside a simple UIView in my view controller. I do this because I want to slide the whole table view to the right just like the facebook app does to expose a menu on the left. So my view hierarchy is:
Main View (Menu View Controller)
- – UIView Which Holds the UITableView (newsFeedView)
- – - – UITableView (newsFeedTableView)
My view controller is set to be the UITableView's datasource and delegate, and it is populating perfectly. The problem I am now having with the refresh control is this line:
self.refreshControl = refresh;
The error I get is: Property 'refreshControl' not found on object of type 'MenuViewController *'
Maybe this is because MenuViewController is just a UIViewController and not a UITableViewController?
Thanks for all your help,
Matt
Matt -
April 21, 2013 at 4:22 pm
Okay thank you I will check it out!
J Shapiro -
April 21, 2013 at 3:50 pm
Matt: That's the problem… the refreshControl property is only available with the TableViewController. This article might help: http://stackoverflow.com/questions/12497940/uirefreshcontrol-without-uitableviewcontroller -Jason