Created
March 23, 2022 12:55
-
-
Save shawn-frank/8b2ab9dc4f2b53b6c163d725bbe0fdac to your computer and use it in GitHub Desktop.
This is a paging example using UITableView with local data when the user scrolls to the end of the list. This example is for iOS using Swift and was created as an answer for this stack overflow question: https://stackoverflow.com/q/71574920/1619193
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// TableScrollVC.swift | |
// TestApp | |
// | |
// Created by Shawn Frank on 23/03/2022. | |
// | |
import UIKit | |
class TableScrollVC: UITableViewController { | |
let tableViewCellIdentifier = "cell" | |
var pageStart = 0 | |
var pageSize = 15 | |
// scrollViewDidScroll will be called several times | |
// so we need to be able to block the loading process | |
var isLoadingItems = false | |
// Array of 50 values which you get from the API | |
var totalNumberOfItems = (0...49).reduce([Int]()) | |
{ (result, number) in | |
var array = result | |
array.append(number) | |
return array | |
} | |
// This will periodically be filled with items from totalNumberOfItems | |
var itemsToLoad: [Int] = [] | |
override func viewDidLoad() | |
{ | |
super.viewDidLoad() | |
title = "Table Scroll Load" | |
// Register a default UITableView Cell | |
tableView.register(UITableViewCell.self, | |
forCellReuseIdentifier: tableViewCellIdentifier) | |
} | |
override func viewDidAppear(_ animated: Bool) { | |
super.viewDidAppear(true) | |
loadItems() | |
} | |
private func loadItems(withDelay delay: Double = 0) { | |
// Check that there are items to load | |
if pageStart < totalNumberOfItems.count, !isLoadingItems { | |
isLoadingItems = true | |
// The delay and dispatch queue is just to show you the | |
// loading of data in batches, it is not needed for the | |
// solution | |
if delay > 0 { | |
presentLoader() | |
} | |
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { | |
if delay > 0 { | |
// dismiss the loader | |
self.dismiss(animated: true, completion: nil) | |
} | |
// Calculate the page end based on the page size or on | |
// the number of items remaining to load if the page size | |
// is greater than the number of items remaining | |
var pageEnd = self.pageStart + self.pageSize - 1 | |
if pageEnd > self.totalNumberOfItems.count { | |
let remainingItems = self.totalNumberOfItems.count - self.pageStart - 1 | |
pageEnd = self.pageStart + remainingItems | |
} | |
let newItems = Array(self.totalNumberOfItems[self.pageStart ... pageEnd]) | |
self.itemsToLoad.append(contentsOf: newItems) | |
self.pageStart = pageEnd + 1 | |
let indexPaths = newItems.map { IndexPath(row: $0, | |
section: 0) } | |
// Update the table view | |
self.tableView.beginUpdates() | |
self.tableView.insertRows(at: indexPaths, with: .fade) | |
self.tableView.endUpdates() | |
// scroll to first newly inserted row | |
if let firstNewRow = indexPaths.first { | |
self.tableView.scrollToRow(at: firstNewRow, | |
at: .top, | |
animated: true) | |
} | |
// Unlock the loading process | |
self.isLoadingItems = false | |
} | |
} | |
} | |
override func tableView(_ tableView: UITableView, | |
numberOfRowsInSection section: Int) -> Int { | |
return itemsToLoad.count | |
} | |
override func tableView(_ tableView: UITableView, | |
cellForRowAt indexPath: IndexPath) -> UITableViewCell | |
{ | |
let cell | |
= tableView.dequeueReusableCell(withIdentifier: tableViewCellIdentifier)! | |
cell.textLabel?.text = "Row \(indexPath.row + 1)" | |
return cell | |
} | |
override func scrollViewDidScroll(_ scrollView: UIScrollView) { | |
// Check that the current scroll offset is > 0 so you don't get | |
// any false positives when the table view is set up | |
// and check if the current offset has reached the end | |
if scrollView.contentOffset.y >= 0 && | |
scrollView.contentOffset.y >= (scrollView.contentSize.height - scrollView.frame.size.height) { | |
print("reached end, load more") | |
// Load more data | |
loadItems(withDelay: 1) | |
} | |
} | |
private func presentLoader() { | |
let alert = UIAlertController(title: nil, | |
message: "Please wait...", | |
preferredStyle: .alert) | |
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, | |
y: 5, | |
width: 50, | |
height: 50)) | |
loadingIndicator.hidesWhenStopped = true | |
loadingIndicator.style = UIActivityIndicatorView.Style.medium | |
loadingIndicator.startAnimating(); | |
alert.view.addSubview(loadingIndicator) | |
present(alert, animated: true, completion: nil) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment