Last active
August 29, 2015 14:19
-
-
Save nemotoo/36394830b8e03509c47f to your computer and use it in GitHub Desktop.
Seamless Infinite UITableView with double page data
This file contains hidden or 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
// Reference - Main : http://stackoverflow.com/questions/18164519/uitableview-inserting-section-at-top-while-scrolling | |
// Reference - Sub : http://bharath2020.in/2012/10/01/uitableview-tricks-part-2-infinite-scrolling/ | |
import UIKit | |
class SeamlessInfiniteTableViewController: UITableViewController { | |
/* Variables */ | |
let FetchingCount = 10 //WARNING : Below 10 is not recommened and tested | |
var mergedSections:[[String]] = [] //WARNING : Every section has more than 0 row | |
/* */ | |
/* Private */ | |
var numOfBackItems = 0 | |
var backSections:[[String]] = [] | |
var numOfFrontItems = 0 | |
var frontSections:[[String]] = [] | |
/* */ | |
/* Debug */ | |
let numOfDummySections = 3 | |
let numOfDummyRows = 5 | |
/* */ | |
var page = INT_MAX{ | |
didSet{ | |
if page == oldValue { | |
return | |
}else if page - 1 == oldValue{ | |
//Next Page | |
println("Next Page") | |
backSections = frontSections | |
numOfBackItems = numOfFrontItems | |
loadFrontItems() | |
}else if page + 1 == oldValue { | |
// Previous Page | |
println("Previous page") | |
frontSections = backSections | |
numOfFrontItems = numOfBackItems | |
loadBackItems() | |
}else{ | |
println("Load all pages") | |
loadBackItems() | |
loadFrontItems() | |
} | |
mergedSections = backSections + frontSections | |
} | |
} | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
page = 0 | |
if mergedSections.count == 0{ | |
return | |
} | |
var initialSection = backSections.count // Last index of backSections + 1 | |
var initialRow = 0 | |
if frontSections.count == 0 { | |
initialSection = backSections.count - 1 | |
initialRow = backSections.last!.count - 1 | |
} | |
var initialIndexPath = NSIndexPath(forRow: initialRow, inSection: initialSection) | |
tableView.scrollToRowAtIndexPath(initialIndexPath, atScrollPosition: UITableViewScrollPosition.Top, animated: false) | |
} | |
override func didReceiveMemoryWarning() { | |
super.didReceiveMemoryWarning() | |
// Dispose of any resources that can be recreated. | |
} | |
override func prefersStatusBarHidden() -> Bool { | |
return true | |
} | |
override func numberOfSectionsInTableView(tableView: UITableView) -> Int { | |
return mergedSections.count | |
} | |
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | |
return mergedSections[section].count | |
} | |
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { | |
var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell! | |
cell.textLabel!.text = mergedSections[indexPath.section][indexPath.row] | |
return cell | |
} | |
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { | |
return mergedSections[section][0] | |
} | |
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { | |
if (tableView.contentSize.height < tableView.frame.size.height) { | |
println("TableView is smaller than frame") | |
return | |
} | |
var visibleIndexPaths = tableView.indexPathsForVisibleRows() as! [NSIndexPath] | |
if visibleIndexPaths.count == 0{ | |
println("TableView has no visiable rows") | |
return | |
} | |
var pageStepper = 0 | |
// 1. Check Top | |
if indexPath.section == 0 && indexPath.row == 0{ | |
if numOfBackItems < FetchingCount{ | |
println("[Top] There is no previous items") | |
return | |
} | |
println("[Top] Load previous page") | |
pageStepper = -1 | |
} | |
// 2. Check Bottom | |
else if indexPath.section == (mergedSections.count - 1) && indexPath.row == (mergedSections.last!.count - 1){ | |
if numOfFrontItems < FetchingCount{ | |
println("[Bottom] There is no next items") | |
return | |
} | |
println("[Bottom] Load next page") | |
pageStepper = 1 | |
} | |
if pageStepper == 0{ | |
return | |
} | |
dispatch_async(dispatch_get_main_queue(), { () -> Void in | |
self.page += pageStepper | |
var oldIndexPath = indexPath | |
var before = self.tableView.rectForRowAtIndexPath(oldIndexPath) | |
var contentOffset = self.tableView.contentOffset | |
self.tableView.reloadData() | |
var newIndexPath = NSIndexPath(forRow: oldIndexPath.row, inSection: oldIndexPath.section - pageStepper) | |
var after = self.tableView.rectForRowAtIndexPath(newIndexPath) | |
contentOffset.y += (after.origin.y - before.origin.y); | |
self.tableView.contentOffset = contentOffset | |
}) | |
} | |
//MARK: - Private | |
func loadBackItems(){ | |
backSections = [] | |
//Load dummy back items | |
for s in 0..<numOfDummySections{ | |
var section:[String] = [] | |
for r in 0 ..< numOfDummyRows{ | |
section.append(("[Page \(page - 1)]\(s) / \(r)")) | |
} | |
backSections.append(section) | |
} | |
numOfBackItems = countItems(backSections) | |
} | |
func loadFrontItems(){ | |
frontSections = [] | |
//Load dummy front items | |
for s in 0..<numOfDummySections{ | |
var section:[String] = [] | |
for r in 0 ..< numOfDummyRows{ | |
section.append(("[Page \(page)]\(s) / \(r)")) | |
} | |
frontSections.append(section) | |
} | |
numOfFrontItems = countItems(frontSections) | |
} | |
func countItems(twoDimensionArray:[[String]]) -> Int{ | |
var count = 0 | |
for ar in twoDimensionArray{ | |
count += ar.count | |
} | |
return count | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I recommend to use JTTableViewController