27

I want to add a ActivityIndicator to the bottom of my UITableView when the last cell is displayed, while I'm fetching more data, then when the data got fetched hide it.

So I scroll to the bottom -> last row displayed -> spinner starts spinning while data is fetched -> Data fetched, hide the spinner -> new data added to the tableview.

Any tips on how can I achieve this?

Thanks ;)

Ivan Cantarino
  • 3,058
  • 4
  • 34
  • 73

6 Answers6

70

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.

LeoGalante
  • 747
  • 6
  • 14
AyAz
  • 2,027
  • 2
  • 21
  • 28
15

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
    }
}
Imran Rasheed
  • 825
  • 10
  • 25
Sachin Rasane
  • 1,501
  • 12
  • 17
9

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
    }
}
Vasil Garov
  • 4,851
  • 1
  • 26
  • 37
  • What is going on in the initializer `CustomTableViewDataSource(query: CustomQuery)`? Maybe you could add the code for CustomTableViewDataSource in your answer? – Knut Valen Feb 05 '20 at 09:33
  • The initializer is something like this? `init(query: CustomQuery, completionHandler: @escaping (Result<(), Error>) -> Void) { ... }` – Knut Valen Feb 05 '20 at 09:58
  • 2
    Something similar. Just adjust `Result` enum to your needs. My completion handler looks like this: `@escaping (Result) -> Void`. If you would like to learn more about this technique and the data source check this out: https://codelabs.developers.google.com/codelabs/firebase-cloud-firestore-workshop-swift/#4 – Vasil Garov Feb 05 '20 at 10:09
  • I needed to change it to `@escaping (Result) -> Void` to not get syntax errors. – Knut Valen Feb 05 '20 at 10:17
2

One way would be to add a custom cell with a UIActivityIndicatorView in it. You could put it in a separate section. There are many ways to do it.

Or you check this out: https://github.com/evnaz/ENFooterActivityIndicatorView

Lukas Würzburger
  • 6,543
  • 7
  • 41
  • 75
0
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {

    let total = (self.array.count )
    if (indexPath.item + 1) == (self.array.count ){
        self.limit = Int(self.limit+20) // table item render limit
        if total < limit {
            let spinner = UIActivityIndicatorView(style: .gray)
            spinner.startAnimating()
            spinner.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: tableView.bounds.width, height: CGFloat(44))

            self.myTableView.tableFooterView = spinner
            self.myTableView.tableFooterView?.isHidden = false
        }
    }
}
Sumona Salma
  • 127
  • 2
  • 14
0

You can add subview to UITableView and show it when user scrolls to bottom.

I had to implement same thing on multiple UIScrollViews and ended up creating a DSL for it.

Also created a pod
You can take a look at the source code or use the pod if you want.

https://github.com/salmaanahmed/PullToRefreshDSL

Salmaan
  • 3,543
  • 8
  • 33
  • 59