Skip to content

Instantly share code, notes, and snippets.

@shawn-frank
Created March 23, 2022 12:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shawn-frank/8b2ab9dc4f2b53b6c163d725bbe0fdac to your computer and use it in GitHub Desktop.
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
//
// 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