Pull-to-refresh is a UI effect that you’ll use over and over. I’ll walk you through the simplest implementation where the scrolling distance controls the alpha and rotation of the refresh icon.


Hamburger Menu



You can download my project on GitHub.

To begin you’ll need a UITableView with some dummy cells. Add a UIImageView for the loading icon on top of the UITableView. Set the alpha in InterfaceBuilder to 0.




Then in assets.xcassets add a series of images for the loading spinner. I found a gif online and split it into its component images and then added them inside assets.xcassets. The more images you use the smoother the animation will look. Name them with a similarly uniform naming pattern because we will need to load them as UIImage programatically.




UITableView is a subclass of… that’s right, UIScrollView. To access the ContentOffset (the amount the user has scrolled) implement UIScrollViewDelegate and add this delegate method to the ViewController.

Here is where the magic happens. The three constants allow you to customize the animation. The alpha and rotation are based on the scrollView.contentOffset.y.

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if scrollView.contentOffset.y <= 0 {
        // 0.0 - 1.0: larger numbers delay how far down the pull needs to be before the spinner appears.
        let loadingAppearanceOffset: CGFloat = 0.2
        // 1 - 5: larger numbers slow down the rotation speed of the spinner.
        let loadingAppearanceSpeed: CGFloat = 3
        // Set with the number of images in assets.xcassets. More images will make for a smoother animation.
        let loadingNumberOfImages: Int = 20
        let alpha: CGFloat = (abs(scrollView.contentOffset.y) / 100) - loadingAppearanceOffset
        let imageNumber = Int(abs(scrollView.contentOffset.y) / loadingAppearanceSpeed) % loadingNumberOfImages
        let imageName = "loading" + String(imageNumber)
        let image = UIImage(named: imageName)
        loadingImageView.alpha = min(alpha, 1.0)
        loadingImageView.image = image
        loadingImageView.tintColor = UIColor.init(hexString: "C9CBDE")



You’ll also need to implement scrollViewDidEndDragging to know when to make your API calls. I’ve hardcoded -20 as a minimum amount of down-pull required to activate pull-to-refresh. You can customize this as you like.

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    if scrollView.contentOffset.y <= -20 {
        // A pull to refresh occured. Make your network calls.

Download Sample Project on GitHub. MIT License.