Pull-to-refresh has become a common user interface paradigm in the mobile space. It made its introduction several years ago in Tweetie, a very popular Twitter client created by Loren Brichter.

The idea is simple. The user pulls down to refresh the contents of a table or collection view. Since the addition of the UIRefreshControl class in iOS 6, pull-to-refresh has become very easy to add to table views, collection views, and even scroll views.

Jump Start

To speed things up, I created an Xcode project that fetches weather data from the Dark Sky API and displays the weather data for the coming days in a table view. Clone or download the project from GitHub if you want to follow along. Take a moment to browse the project.

Project Setup

When the user pulls the table view down, the application should send another fetch request to the Dark Sky API, show a refresh control, and update the table view when the fetch request is completed. Let's start by adding a refresh control to the table view.

Adding a Refresh Control

Open the project and select ViewController.swift in the Project Navigator on the right. In this view controller, the weather data is fetched from the Dark Sky API and displayed in a table view.

The first thing we need to do is create an instance of the UIRefreshControl class. Declare a private constant property at the top and assign an instance of the UIRefreshControl class to it.

private let refreshControl = UIRefreshControl()

The refresh control is not part of the view hierarchy yet. We need to add it to the table view. Navigate to the setupTableView() method of the ViewController class and add the following snippet to add the refresh control to the table view of the view controller.

// Add Refresh Control to Table View
if #available(iOS 10.0, *) {
    tableView.refreshControl = refreshControl
} else {
    tableView.addSubview(refreshControl)
}

It may look a bit more complex than you expected. The reason is simple. Since iOS 10, the UITableView and UICollectionView classes have a refreshControl property. You can add a refresh control to a table or collection view by assigning an instance of the UIRefreshControl class to this property.

If your application targets versions prior to iOS 10, you simply add the refresh control as a subview to the table view. The table view takes care of the rest. It knows what to do when a UIRefreshControl instance is added as a subview.

Adding a Target and Action

We're not done yet. The UIRefreshControl is a UIControl subclass. To make it work, we need to add a target and an action for the valueChanged event. This is easy and something you're probably already familiar with.

// Configure Refresh Control
refreshControl.addTarget(self, action: #selector(refreshWeatherData(_:)), for: .valueChanged)

We add the view controller as a target for the refreshWeatherData(_:) action. The action is triggered when the valueChanged event occurs, that is, when the user pulls and releases the table view.

The implementation of refreshWeatherData(_:) is straightforward. The view controller fetches new weather data from the Dark Sky API by invoking fetchWeatherData().

@objc private func refreshWeatherData(_ sender: Any) {
    // Fetch Weather Data
    fetchWeatherData()
}

Updating the User Interface

In fetchWeatherData(), we update the user interface by invoking updateView(). But we also need to tell the refresh control that the fetch request completed and that it can stop refreshing. We do this by invoking endRefreshing() on the refresh control.

private func fetchWeatherData() {
    dataManager.weatherDataForLocation(latitude: 37.8267, longitude: -122.423) { (location, error) in
        DispatchQueue.main.async {
            if let location = location {
                self.days = location.days
            }

            self.updateView()
            self.refreshControl.endRefreshing()
            self.activityIndicatorView.stopAnimating()
        }
    }
}

Customizing the Refresh Control

It's possible to customize a UIRefreshControl instance to some extent. You can, for example, set the tint color of the refresh control.

refreshControl.tintColor = UIColor(red:0.25, green:0.72, blue:0.85, alpha:1.0)

You can also show a message to the user by setting the attributedTitle property of the refresh control.

refreshControl.attributedTitle = NSAttributedString(string: "Fetching Weather Data ...", attributes: attributes)

Customizing a Refresh Control

But that's about all the customization you can do to a refresh control. While UIRefreshControl is a convenient class and very easy to use, it's limited in terms of customizability.