Swift tableView Pagination
For that you need to have server side change also.
Server will accept
fromIndex
andbatchSize
in theAPI
url as query param.let listUrlString = "http://bla.com/json2.php?listType=" + listType + "&t=" + NSUUID().UUIDString + "&batchSize=" + batchSize + "&fromIndex=" + fromIndex
In the server response, there will be an extra key
totalItems
. This will be used to identify all items are received or not. An array or itemsfromIndex
tobatchSize
number of items.
In the app side
First
loadItem()
will be called withfromIndex = 0
andbatchSize = 20
(for example inviewDidLoad()
orviewWillAppear
). removeAll items fromprivateList
array before callingloadItem()
for the first timeServer returns an array of first 20 items and
totalItems
total number of items in the server.Append the 20 items in
privateList
array and reloadtableView
In
tableView:cellForRowAtIndexPath
method check if the cell is the last cell. And check iftotalItems
(form server) is greater thanprivateList.count
. That means there are more items in the server to loadif indexPath.row == privateList.count - 1 { // last cell
if totalItems > privateList.count { // more items to fetch
loadItem() // increment `fromIndex` by 20 before server call
}
}
Question: where is refresh ? will be scrolling ?
Refresh after appending new items in the array when server response received. (step 3)
Scrolling will trigger tableView:cellForRowAtIndexPath
for every cell when user scrolls. Code is checking if it is the last cell and fetch remaining items. (step 4)
Sample project added:
https://github.com/rishi420/TableViewPaging
Swift 4 tableView API Pagination
There were many flaws in your logic, so I have rewritten it with minimal changes. Now it will serve your purpose.
import UIKit
struct PagedCharacters: Codable {
let info: Info
let results: [Results]
}
struct Info: Codable {
let count: Int
let pages: Int
let next: String
let prev: String
}
struct Results: Codable {
let name: String
let status: String
let species: String
}
And here is the code for view controller class:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet var uiTableView: UITableView!
var aryDownloadedData:[Results]?
var nextPageUrl:String!
override func viewDidLoad() {
super.viewDidLoad()
getIntitalRickAndMortyData()
self.uiTableView.dataSource = self
self.uiTableView.delegate = self
// Do any additional setup after loading the view.
}
func getIntitalRickAndMortyData(){
aryDownloadedData = []
//here first page is next page
nextPageUrl = "https://rickandmortyapi.com/api/character/"
getRickAndMortyData()
}
func getRickAndMortyData() {
//construct the url, use guard to avoid nonoptional
guard let urlObj = URL(string: nextPageUrl) else
{ return }
//fetch data
URLSession.shared.dataTask(with: urlObj) {[weak self](data, response, error) in
//to avoid non optional in JSONDecoder
guard let data = data else { return }
do {
//decode object
let downloadedRickAndMorty = try JSONDecoder().decode(PagedCharacters.self, from: data)
self?.aryDownloadedData?.append(contentsOf: downloadedRickAndMorty.results)
self?.nextPageUrl = downloadedRickAndMorty.info.next
DispatchQueue.main.async {
self?.uiTableView.reloadData()
}
print(self?.aryDownloadedData as Any)
} catch {
print(error)
}
}.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.aryDownloadedData?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let count = aryDownloadedData?.count, count>1{
let lastElement = count - 1
if indexPath.row == lastElement {
//call get api for next page
getRickAndMortyData()
}
}
guard let cell = tableView.dequeueReusableCell(withIdentifier: "rickandmortyCell") as? CharacterTableViewCell else { return UITableViewCell() }
cell.nameLabel.text = "Name: " + (self.aryDownloadedData?[indexPath.row].name ?? "default")
return cell
}
}
How to add pagination in UITableView.?
There are many way to achieve this like following way:
- First you need to create one global
NSMutableArray
that load inTableView
Delegates andDataSaurce
. - Now on your api call you need to add That records in global Array.
- On last cell or the
tableview
you can call it again with next page URL based on your API and reload table on each Call - You can use for loadMore many thirdParty library available on github:
https://github.com/OpenFibers/DragRefreshAndLoadMoreTableDemo
https://github.com/Abizern/PartialTable
https://github.com/robertmryan/UITableView-Bottom-Refresh
Based On your code:
in .h class
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
{
NSURL* nextURL;
int currentPage;
}
in .M Class
- (void)viewDidLoad {
currentPage = 1;
[self LoadData];
[super viewDidLoad];
}
-(void)LoadData
{
NSURL *theURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://qa.networc.in:1336/api/dispatcher/rideScheduled/:%d",currentPage]];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:theURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:20.0f];
//Specify method of request(Get or Post)
[theRequest setHTTPMethod:@"GET"];
//Pass some default parameter(like content-type etc.)
[theRequest setValue:@"application/json" forHTTPHeaderField:@"Accept"];
[theRequest setValue:@"application/json; charset=UTF-8" forHTTPHeaderField:@"Content-Type"];
[theRequest addValue:token forHTTPHeaderField:@"x-access-token"];
NSURLResponse *theResponse = NULL;
NSError *theError = NULL;
NSData *responseData = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:&theResponse error:&theError];
if (responseData == nil)
{
return;
}
NSDictionary *dataDictionaryResponse = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&theError];
NSLog(@"url to send request= %@",theURL);
NSLog(@"%@",dataDictionaryResponse);
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:theRequest
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(@"Response:%@ %@\n", response, error);
if(error == nil)
{
NSString * text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(@"Data = %@",text);
}
currentPage ++;
dictionary = [[NSJSONSerialization JSONObjectWithData:data options:0 error:nil]objectForKey:@"data"];
NSArray *total = [dictionary objectForKey:@"total"];
NSLog(@"latlop %@", total);
[getid addObjectsFromArray:total];
[self updateTable];
}];
[task resume];
}
Infinite scrolling pagination of the TableView
You can see that you have reached the bottom of the list with willDisplay
function from the UITableViewDelegate
protocol. like below code:
public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forItemAt indexPath: IndexPath) {
let lastIndex = self.popularMoviesArray.count - 1
if indexPath.row == lastIndex {
// api call and get next page
}
}
and you can call API to get the next page data.
then append new data to your data source list and reload tableView:
self.popularMoviesArray.append(contentsOf: newListData)
self.tableView.reloadData()
UITableView Pagination - Bottom Refresh to Load New Data in Swift
Nope, you can't go with that approach because cellForRowAtIndexPath
is called many times and also it will take much time to check your conditions!
Here, I have found a better option for UITableView
pagination.
func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
//Bottom Refresh
if scrollView == tableView{
if ((scrollView.contentOffset.y + scrollView.frame.size.height) >= scrollView.contentSize.height)
{
if !isNewDataLoading{
if helperInstance.isConnectedToNetwork(){
isNewDataLoading = true
getNewData()
}
}
}
}
}
isNewDataLoading
is Bool
to check that UITableView
is loading new data or not!
Hope this helps!
Related Topics
Passing Arguments to @Selector Method
How to Deal with Non-Optional Values in Nsuserdefaults in Swift
How to Make One Side of a Div Pointy with CSS
iOS Wkwebview Swift JavaScript Enable
Nsurlprotocol Isn't Asked to Load After Yes Response to Caninitwithrequest
Uiactivityviewcontroller => Launchservices: Invalidationhandler Called
Mpvolumeview Does Not Show Route Button on Launch
Passing Data Between View Controllers: from UItableview to a Details View Controller
Multiple Lines in UItabbaritem Label
Google Signin Not Calling Delegate Method After Success
Cannot Subscript a Value of [Anyobject]? with an Index of Type Int
iOS 8 and Xcode 6 Simulator Display Out of Alignment
Viewdiddisappear Not Called When Use Presentviewcontroller
How to Fetch Images from Photo Library Within Range of Two Dates in iOS
Can't Use @Observedobject on Real iPhone