iOS UIRefreshControl on UITableView

Filed Under: iOS

In this tutorial, we’ll be implementing UIRefreshControl on the UITableView in our iOS Application.
You must have seen very often a spinning wheel when you pull the top of your screen in your iOS Applications. We’ll be implementing the same.

UIRefreshControl

A UIRefreshControl is a standard UI control element that is a part of the UI kit and is used to reload/refresh TableView, CollectionView or a ScrollView.

Today, we’ll stick to UITableView only. Since iOS 10, UITableView consists of a refreshControl property which makes it easy to attach the UIRefreshControl element to the UITableView.

The UIRefreshControl consists of a progress indicator icon. We can optionally add a title message as well besides setting the background color of the UIRefreshControl.

UITableView is a subclass of UIScrollView hence the refreshControl property is already present in ScrollView.

To create a UIRefreshControl instance in Swift we do:


let myRefreshControl = UIRefreshControl()

We can then add this view to the UITableView as:


self.myTableView.refreshControl = myRefreshControl

//or

self.myTableView.addSubView(myRefreshControl) 

We can set the title and font color on the UIRefreshControl in the following manner:


let myString = "Pull to refresh"
let myAttribute = [NSAttributedString.Key.foregroundColor: UIColor.white]
let myAttrString = NSAttributedString(string: myString, attributes: myAttribute)
refreshControl.attributedTitle = myAttrString

How to call a function when the UIRefreshControl is pulled?
Set the function inside the addTarget function invoked over the UIRefreshControl.


refreshControl.addTarget(self, action: #selector(ViewController.handleRefresh), for: UIControl.Event.valueChanged)

Which function gets triggered when the pull to refresh gesture is performed?

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)

Inside this function we can manually call a function depending on how fast the UITableView was pulled to refresh.
Sometimes the user doesn’t actually want to refresh. In these cases, they’re just slowly pulling the UITableView down and slowly leaving it to return to its original position.
In these cases, the velocity is less. We can decide to not trigger an action in such cases manually from this function.

Let’s create a new Single View Application in XCode.

ios ui refreshcontrol tableview project

In the Main.storyboard, add a UITableView to the View Controller, set the constraints. Set the delegates. Add the prototype cell and set the cell identifier as shown below:

ios ui refreshcontrol tableview storyboard

Connect the TableView to the ViewController.swift file.

Now add the following code inside the ViewController.swift file:


import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var myTableView: UITableView!
    var myArray = ["apple","windows","amazon"]
    
    lazy var refreshControl: UIRefreshControl = {
        let refreshControl = UIRefreshControl()
        refreshControl.addTarget(self, action: #selector(ViewController.handleRefresh), for: UIControl.Event.valueChanged)
        
        return refreshControl
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        myTableView.refreshControl = refreshControl
        //myTableView.addSubview(refreshControl)        
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = myArray[indexPath.row]
        return cell
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return myArray.count
    }
    
    @objc func handleRefresh()
    {
        let newElements = ["google","facebook","flipkart"]
        myArray.append(contentsOf: newElements)
        myArray.append(String(myArray.count))
        myTableView.reloadData()
        refreshControl.endRefreshing()
        
    }


}

UIRefreshControl is created using the lazy var property in Swift.
endRefreshing is called to close the UIRefreshControl.

In the above code, we’ve set an Array of strings in the TableView.
Every time we refresh, we’ve added more elements in the UITableView.

The output of the application in action is given below:

ios ui refreshcontrol tableview output 1

Let’s customise the UIRefreshControl now:

Change the viewDidLoad method to:


override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        myTableView.refreshControl = refreshControl
        let myString = "Pull to refresh"
        let myAttribute = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 26)]
        let myAttrString = NSAttributedString(string: myString, attributes: myAttribute)
        refreshControl.attributedTitle = myAttrString
        refreshControl.tintColor = UIColor.white
        refreshControl.backgroundColor = UIColor.black
        
    }

We’ve added a tint color on the spinning wheel alongwith a background color on the complete UI control element.

The output of the application in action is given below:

ios ui refreshcontrol tableview output 2

Remove the addTarget line of code from UIRefreshControl.
Add the following code inside the ViewController class:


func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        print(velocity)
        
        if(velocity.y < -0.1)
        {
            handleRefresh()
        }
    }

We've noticed that the faster you pull the UIRefreshControl down, the higher the velocity would be in negative value. So we've added a condition that doesn't trigger the handleRefresh function until the value is in a negative value.

The output of the application in action is given below:

ios ui refreshcontrol tableview output 3

As it's visible, the values aren't getting updated since handleRefresh didn't trigger.
This is because we are not pulling it down and leaving the touch. We are still holding it while it goes back up which leads to a positive velocity.

This brings an end to this tutorial. You can download the project from the link below:

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages