Sunday, 23 September 2012

iOS 6 UIRefreshControl with MonoTouch

Another cute little new feature of iOS 6 is the built-in pull-to-refresh control - UIRefreshControl. It is really simple to wire up with Xamarin on a regular UITableViewController or with MonoTouch.Dialog.

In case you're unfamiliar with it, this is how the new control looks:

To implement:

  • Assign the RefreshControl property of a UITableViewController to a new instance of the control, eg. RefreshControl = new UIRefreshControl();
  • Create a handler for the control's ValueChanged event, eg RefreshControl.ValueChanged += HandleValueChanged;. In this handler your code will do whatever is required to refresh the data when the user pulls down enough to trigger the event.
  • In HandleValueChanged code, once you have refreshed the table, call the EndRefreshing method of the control to stop it spinning. Your refresh code was probably not running on the main thread, in which case you'll probably want to use InvokeOnMainThread like this: InvokeOnMainThread (() => {RefreshControl.EndRefreshing (); });

There are additional features you may use:

  • BeginRefreshing - call this method from your code if you start a refresh operation from code (eg. on a timer or in response to some other event). This sets the UI of the control to indicate there is already a refresh in progress.
  • Refreshing - whether there is already a refresh in progress.
  • AttributedTitle - optional text that appears under the refresh control.
  • TintColor - customize the appearance of the control to match your application's theme.

Refer to Apple's UIRefreshControl doc for additional info.

UPDATE: What about iOS 5?

This above example will not work on earlier versions of iOS - the UIRefreshControl class does not exist, nor does the RefreshControl property on UITableViewController. To get around this problem, the following code does a system check and falls back to an old-fashioned navbarbutton in earlier versions of iOS:

if (UIDevice.CurrentDevice.CheckSystemVersion (6,0)) {
    // UIRefreshControl iOS6
    RefreshControl = new UIRefreshControl();
    RefreshControl.ValueChanged += (sender, e) => { Refresh(); };
} else {
    // old style refresh button
    NavigationItem.SetRightBarButtonItem (new UIBarButtonItem (UIBarButtonSystemItem.Refresh), false);
    NavigationItem.RightBarButtonItem.Clicked += (sender, e) => { Refresh(); };
}

The Refresh method should contain the code that actually gets new data and updates the UITableView. That method should contain a similar if (CheckSystemVersion(6,0)) clause that wraps the call to the RefreshControl.EndRefreshing method on the main thread. Users on older operating systems will see this:

8 comments:

  1. Great post, Craig!
    One thing I'm curious about: what happens if you run this code on a pre-iOS 6 device? Would it crash or just do nothing?

    ReplyDelete
  2. It'd definitely break :-( I've updated the post with some info on how you can handle backwards-compatibility. HTH.

    ReplyDelete
  3. This method works good with iOS 6, but actually I am concerned whether it works on earlier versions of iOS or not ?. I am using iPhone 4 with iOS 5 installed in it so whether it can work with my iPhone.

    ReplyDelete
    Replies
    1. I added a section to the post to describe what to do in iOS 5 and earlier.

      Delete
  4. Hi Craig,

    Is BeginRefreshing meant to "activate" the UIRefreshControl programmaticaly, when called let's say by a refresh button on the UITableView Controller?

    ReplyDelete
    Replies
    1. BeginRefreshing doesn't visibly 'activate' anything - it just sets the state of the UIRefreshControl. If you call BeginRefreshing programmatically and _then_ the user pulls-down, the UIRefreshControl will show the in-progress spinner rather than the refresh-blob. Also, BeginRefreshing does not call the ValueChanged handler, so you would only call it from code that is already doing the actual data refresh as well.

      Calling EndRefreshing is what tells the UIRefreshControl to hide the spinner and re-enable the blob.

      Delete
  5. Brilliant! Will be using this in my new app :) JD

    ReplyDelete
  6. Hi Craig,

    Thank you for this great tutorial on UIRefreshControl. I have a Pull to Refresh board on Verious with this post. I'd like to share it with anyone interested in learning how to add a refresh control to a table view. Please let me know if there’s anything I can add to make a more comprehensive resource for other developers.
    http://www.verious.com/board/Giancarlo-Leonio/adding-a-pull-to-refresh-to-a-table-view-in-ios-6

    @Veriously

    ReplyDelete