Skip to content

Repository files navigation

JTTableViewController

CI StatusVersionLicensePlatform

A ViewController with a tableView which manage pagination and loaders for iOS.

Installation

With CocoaPods, add this line to your Podfile.

pod 'JTTableViewController', '~> 2.0'

What's in it?

  • avoid parallel requests problem (you start two requests and the first one finish after the second), last request is the only one we want to use
  • easily manage pagination
  • display a view for the first loading (when your tableView is empty)
  • display a view when there is no results to your first request
  • display a loader view (an UITableViewCell) for indicate the next page is loading
  • display a view for errors

Screenshots

Example

Usage

Minimum usage

You can either use JTFullTableViewController which is almost like UITableViewController. Or you can inherit from JTTableViewController,UITableViewDelegate, UITableViewDataSource, create an UITableView, assign it to self.tableView, add it to the self.view and set the delegate and dataSource yourself. If your controller is fullscreen use JTFullTableViewController else use JTTableViewController.

import JTTableViewController
classViewController:JTTableViewController<YourModel>,UITableViewDelegate,UITableViewDataSource{
// Used in this example to manage your pagingation
privatevarcurrentPage=1
// Must be implemented
overridefunc jt_tableView(_ tableView:UITableView, cellForRowAt indexPath:IndexPath)->UITableViewCell{letcell= tableView.dequeueReusableCell(withIdentifier:"cell")letanInstanceOfYourModel=self.results[indexPath.row]
/*
whatever you wanna do with `anInstanceOfYourModel` and your `cell`
*/
return cell
}
// Must be implemented
overridefunc fetchResults(){
super.fetchResults()
currentPage =1
// `lastRequestId` is used to avoid problem with parallel requests
letlastRequestId=self.lastRequestId
YourService.retrieveData(page: currentPage){(error, results)->()iniflet error = error {self.didFailedToFetchResults(error: error, lastRequestId: lastRequestId)return}self.didFetchResults(results: results, lastRequestId: lastRequestId){
// this block is executed if `lastRequestId` matched with `self.lastRequestId`
self.currentPage +=1}}}
// Must be implemented
overridefunc fetchNextResults(){
super.fetchNextResults()
// `lastRequestId` is used to avoid problem with parallel requests
letlastRequestId=self.lastRequestId
YourService.retrieveData(page: currentPage){(error, results)->()iniflet error = error {self.didFailedToFetchResults(error: error, lastRequestId: lastRequestId)}else{self.didFetchNextResults(results: results, lastRequestId: lastRequestId){
// this block is executed if `lastRequestId` matched with `self.lastRequestId`
self.currentPage +=1}}}}}

Advanced usage

import JTTableViewController
classViewController:JTTableViewController<YourModel>,UITableViewDelegate,UITableViewDataSource{
// Used in this example to manage your pagingation
privatevarcurrentPage=1privateletrefreshControl=UIRefreshControl()overridefunc viewDidLoad (){
super.viewDidLoad()
// `nextPageLoaderCell` is an `UITableViewCell`
self.nextPageLoaderCell =MyNextPageLoadCell()
// `fecthResults` is call 5 cells before `nextPageLoaderCell` become visible
self.nextPageLoaderOffset =5
// `noResultsView` is display when `didFetchResults` is called with an `results` empty
letnoResultsView=NoResultsView()self.noResultsView = noResultsView
self.view.addSubview(noResultsView)
// something better than frame with Constraints but not relevant here
noResultsView.frame =self.view.bounds
// `noResultsLoadingView` is display when `fetchResults` is called and `results` is empty
letnoResultsLoadingView=NoResultsLoadingView()self.noResultsLoadingView = noResultsLoadingView
self.view.addSubview(noResultsLoadingView)
// something better than frame with Constraints but not relevant here
noResultsLoadingView.frame =self.view.bounds
// `errorView` is display when `didFailedToFetchResults` is called
leterrorView=ErrorView()self.errorView = errorView
self.view.addSubview(errorView)
// something better than frame with Constraints but not relevant here
errorView.frame =self.view.bounds
refreshControl.addTarget(self, action: #selector(fetchResults), for:.valueChanged)self.tableView?.addSubview(refreshControl)}overridefunc viewWillAppear(_ animated:Bool){
super.viewWillAppear(animated)fetchResults()}
// Must be implemented
overridefunc jt_tableView(_ tableView:UITableView, cellForRowAt indexPath:IndexPath)->UITableViewCell{letcell= tableView.dequeueReusableCell(withIdentifier:"cell")letanInstanceOfYourModel=self.results[indexPath.row]
/*
whatever you wanna do with `anInstanceOfYourModel` and your `cell`
*/
return cell
}
// Must be implemented
overridefunc fetchResults(){self.resetResults()
super.fetchResults()
currentPage =1
// `lastRequestId` is used to avoid problem with parallel requests
letlastRequestId=self.lastRequestId
YourService.retrieveData(page: currentPage){(error, results)->()iniflet error = error {self.didFailedToFetchResults(error: error, lastRequestId: lastRequestId)return}self.didFetchResults(results: results, lastRequestId: lastRequestId){
// this block is executed if `lastRequestId` matched with `self.lastRequestId`
self.currentPage +=1}}}
// Must be implemented
overridefunc fetchNextResults(){
super.fetchNextResults()
// `lastRequestId` is used to avoid problem with parallel requests
letlastRequestId=self.lastRequestId
YourService.retrieveData(page: currentPage){(error, results)->()iniflet error = error {self.didFailedToFetchResults(error: error, lastRequestId: lastRequestId)}else{self.didFetchNextResults(results: results, lastRequestId: lastRequestId){
// this block is executed if `lastRequestId` matched with `self.lastRequestId`
self.currentPage +=1}}}}overridefunc didEndFetching (){
super.didEndFetching()
refreshControl.endRefreshing()}}

You have to implement fetchResults and fetchNextResults methods. They are used to load data (from your web service for example). These methods must call super.

Optionaly, you can override jt_tableView(tableView:heightForRowAt:) for defining the height of cells.

fetchResults is used to retrieve new data (erase all previous data) whereas fetchNextResults is used for get more data (the pagination).

didFetchResults must be call when fetchResults have successfuly retrieve data. didFetchNextResults must be call when fetchNextResults have successfuly retrieve data. didFailedToFetchResults must be call if didFetchResults or didFetchNextResults have failed to retrieve data.

didEndFetching is called after didFetchResults, didFetchNextResults or didFailedToFetchResults

The data are stored in results. Just use self.results to access to them. If you want to remove some elements in results you can use self.unsafeResults, only in specific case (ex: removing one cell).

There are some properties you can customize:

  • nextPageLoaderCell is the loader use for the pagination, it's a UITableViewCell
  • noResultsView is the view display when the results get from your service are empty
  • noResultsLoadingView is the view display when there is no results and you start fetching new data, used for the first load
  • errorView is the view display didFailedToFetchResults is called
  • nextPageLoaderOffset is the number of cells require before the last cell for calling fetchNextResults, by default it's 3

You can also override some methods:

  • didEndFetching
  • showNoResultsLoadingView
  • hideNoResultsLoadingView
  • showNoResultsView
  • hideNoResultsView
  • showErrorView
  • hideErrorView

Sections

JTTableViewController support section management. You just have to override 2 method:

  • numberOfSections(tableView:)
  • jt_tableView(tableView:numberOfRowsInSection:)
import JTTableViewController
classViewController:JTTableViewController<[Int:[YourModel]]>,UITableViewDelegate,UITableViewDataSource{@objc(numberOfSectionsInTableView:)overridefunc numberOfSections(in tableView:UITableView)->Int{returnself.results.count
}overridefunc jt_tableView(_ tableView:UITableView, numberOfRowsInSection section:Int)->Int{returnresults[section].count
}}

Subclassing notes

If you want to subclass JTTableViewController or JTFullTableViewController, the methods from UITableViewDelegate and UITableViewDataSource must have an @objc annotation.

classXXTableViewController<T>:JTTableViewController<T>{
/*
... Here you add whatever you want to add
*/
}classMyViewController:XXTableViewController<YourModel>{
// if you don't add `@objc(tableView:didSelectRowAtIndexPath:)` this method is not called
@objc(tableView:didSelectRowAtIndexPath:)func tableView(_ tableView:UITableView, didSelectRowAt indexPath:IndexPath){/*.../*}}

Requirements

  • iOS 8.0 or higher
  • Swift 3.0

Author

License

JTTableViewController is released under the MIT license. See the LICENSE file for more info.

About

A ViewController for manage pagination and loaders for iOS.

Resources

Stars

61 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages