Add a ActivityIndicator to the bottom of UITableView while loading

Update 2020 Swift 5, Xcode 11

I used @Ayaz Akbar's solution and it worked like charm. It just needed some improvements. Here is how I adopted it to my needs.

Removing the loading indicator if no more items to load

Since you are adding the loading indicator every time you go to the end of the items there will be the case where you no longer add new items to the table view data source. At this point you need to remove the loading indicator. I am using a custom UITableViewDataSource which calls its completion handler every time new data is received. I also keep a var newCount to keep track of the new items count. So here is what I do in my view controller to hide the activity indicator:

private lazy var dataSource: CustomTableViewDataSource = {
    return CustomTableViewDataSource(query: CustomQuery) { [unowned self] (result) in
    // For the initial load I needed a standard activity indicator in the center of the screen
    self.stopMainActivityIndicatorIfNeeded()

    switch result {
    case .success(()):
        if self.dataSource.newCount > 0 {
            self.tableView.reloadData()
        } else {
            // Hide the tableFooterView, respectively the activity indicator
            self.tableView.tableFooterView = nil
        }
    case .failure(let error):
        self.showError(error)
    }
    }
}()

Here is my updated UITableView delegate method:

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    let lastSectionIndex = tableView.numberOfSections - 1
    let lastRowIndex = tableView.numberOfRows(inSection: lastSectionIndex) - 1
    if indexPath.section ==  lastSectionIndex && indexPath.row == lastRowIndex && dataSource.newCount > 0 {
        let spinner = UIActivityIndicatorView(style: .gray)
        spinner.frame = CGRect(x: 0.0, y: 0.0, width: tableView.bounds.width, height: 70)
        spinner.startAnimating()
        tableView.tableFooterView = spinner
    }
}

Add This Function

 func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    let lastSectionIndex = tableView.numberOfSections - 1
    let lastRowIndex = tableView.numberOfRows(inSection: lastSectionIndex) - 1
    if indexPath.section ==  lastSectionIndex && indexPath.row == lastRowIndex {
       // print("this is the last cell")
        let spinner = UIActivityIndicatorView(activityIndicatorStyle: .gray)
        spinner.startAnimating()
        spinner.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: tableView.bounds.width, height: CGFloat(44))

        self.tableview.tableFooterView = spinner
        self.tableview.tableFooterView?.isHidden = false
    }
}

and tableFooterView should hide when data load.


Swift 5

 func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    let lastSectionIndex = tableView.numberOfSections - 1
    let lastRowIndex = tableView.numberOfRows(inSection: lastSectionIndex) - 1
    if indexPath.section ==  lastSectionIndex && indexPath.row == lastRowIndex {
       // print("this is the last cell")
        let spinner = UIActivityIndicatorView(style: .medium)
        spinner.startAnimating()
        spinner.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: tableView.bounds.width, height: CGFloat(44))

        self.tableview.tableFooterView = spinner
        self.tableview.tableFooterView?.isHidden = false
    }
}