Skip to content

Instantly share code, notes, and snippets.

@johndelong
Last active March 28, 2021 00:26
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save johndelong/ed4a7e2a8161f73c7548f74bb9b6f1bd to your computer and use it in GitHub Desktop.
Save johndelong/ed4a7e2a8161f73c7548f74bb9b6f1bd to your computer and use it in GitHub Desktop.
Infinite Scrolling Final
//
// ViewController.swift
// Example
//
// Created by John DeLong on 5/11/16.
// Copyright © 2016 delong. All rights reserved.
//
import UIKit
extension Date {
func dateFromDays(_ days: Int) -> Date {
return (Calendar.current as NSCalendar).date(byAdding: .day, value: days, to: self, options: [])!
}
}
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
let cellBuffer: CGFloat = 2
let cellHeight: CGFloat = 44
let daysToAdd = 30
let dateFormatter = DateFormatter()
lazy var days: [Date] = {
let beginDate = Date().dateFromDays(-29)
let endDate = Date().dateFromDays(30)
return self.generateDays(beginDate, endDate: endDate)
}()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
self.dateFormatter.dateFormat = "MM-dd-yyyy"
}
func generateDays(_ beginDate: Date, endDate: Date) -> [Date] {
var dates: [Date] = []
var date = beginDate
while date.compare(endDate) != .orderedDescending {
dates.append(date)
date = date.dateFromDays(1)
}
return dates
}
}
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.days.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel!.text = self.dateFormatter.string(from: self.days[(indexPath as NSIndexPath).row])
return cell
}
}
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return self.cellHeight;
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let top: CGFloat = 0
let bottom: CGFloat = scrollView.contentSize.height - scrollView.frame.size.height
let buffer: CGFloat = self.cellBuffer * self.cellHeight
let scrollPosition = scrollView.contentOffset.y
// Reached the bottom of the list
if scrollPosition > bottom - buffer {
// Add more dates to the bottom
let lastDate = self.days.last!
let additionalDays = self.generateDays(lastDate.dateFromDays(1), endDate: lastDate.dateFromDays(self.daysToAdd))
self.days.append(contentsOf: additionalDays)
self.days.removeFirst(self.daysToAdd)
// Update the tableView and contentOffset
self.tableView.reloadData()
self.tableView.contentOffset.y -= CGFloat(self.daysToAdd) * self.cellHeight
}
// Reach the top of the list
else if scrollPosition < top + buffer {
// Add more dates to the top
let firstDate = self.days.first!
let additionalDates = self.generateDays(firstDate.dateFromDays(-self.daysToAdd), endDate: firstDate.dateFromDays(-1))
self.days.insert(contentsOf: additionalDates, at: 0)
self.days.removeLast(self.daysToAdd)
// Update the tableView and contentOffset
tableView.reloadData()
self.tableView.contentOffset.y += CGFloat(self.daysToAdd) * self.cellHeight
}
}
}
@rlaferla
Copy link

rlaferla commented Jun 6, 2017

This works if there are no sections. I'd like to know how you would approach a table that has sections with differing numbers of rows per section.

@annjose
Copy link

annjose commented Jan 29, 2018

This gist was very helpful for me to understand how to implement Infinite scrolling. Thank You!

@kentoh
Copy link

kentoh commented May 27, 2018

For infinite scroll up, how would you calculate the contentOffset for the tableview, if the newly added cells have dynamic height?

@bayareahank
Copy link

When run this code with paging enabled (each cell occupying whole page), the offset change applied at end of data manipulation is lost. So when the code comes back to compare scroll again, the data change situation will be triggered again, making scroll jump by numToAdd each time. If we separate the data changes and apply only one each time, be it remove or append, the offset change will be carried over and scroll will work perfectly.

Run this with Xcode 10.2, Swift 5. Not sure whether the author or anybody else has seen similar problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment