Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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

This comment has been minimized.

Copy link

@rlaferla 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

This comment has been minimized.

Copy link

@annjose annjose commented Jan 29, 2018

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

@kentoh

This comment has been minimized.

Copy link

@kentoh 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

This comment has been minimized.

Copy link

@bayareahank bayareahank commented Apr 4, 2019

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