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
}
}