{"id":19856963,"url":"https://github.com/johnvuko/jttableviewcontroller","last_synced_at":"2025-05-02T02:30:27.700Z","repository":{"id":30464317,"uuid":"34018242","full_name":"johnvuko/JTTableViewController","owner":"johnvuko","description":"A ViewController for manage pagination and loaders for iOS.","archived":false,"fork":false,"pushed_at":"2019-01-17T17:05:13.000Z","size":1134,"stargazers_count":61,"open_issues_count":0,"forks_count":9,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-06T21:01:51.052Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/johnvuko.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-04-15T20:57:15.000Z","updated_at":"2023-12-19T17:37:41.000Z","dependencies_parsed_at":"2022-08-20T21:20:28.573Z","dependency_job_id":null,"html_url":"https://github.com/johnvuko/JTTableViewController","commit_stats":null,"previous_names":["johnvuko/jttableviewcontroller"],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnvuko%2FJTTableViewController","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnvuko%2FJTTableViewController/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnvuko%2FJTTableViewController/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnvuko%2FJTTableViewController/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/johnvuko","download_url":"https://codeload.github.com/johnvuko/JTTableViewController/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251972408,"owners_count":21673599,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T14:17:04.728Z","updated_at":"2025-05-02T02:30:27.150Z","avatar_url":"https://github.com/johnvuko.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JTTableViewController\n\n[![CI Status](http://img.shields.io/travis/jonathantribouharet/JTTableViewController.svg)](https://travis-ci.org/jonathantribouharet/JTTableViewController)\n![Version](https://img.shields.io/cocoapods/v/JTTableViewController.svg)\n![License](https://img.shields.io/cocoapods/l/JTTableViewController.svg)\n![Platform](https://img.shields.io/cocoapods/p/JTTableViewController.svg)\n\nA ViewController with a tableView which manage pagination and loaders for iOS.\n\n## Installation\n\nWith [CocoaPods](http://cocoapods.org/), add this line to your Podfile.\n\n    pod 'JTTableViewController', '~\u003e 2.0'\n\n## What's in it?\n\n- 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\n- easily manage pagination \n- display a view for the first loading (when your `tableView` is empty)\n- display a view when there is no results to your first request\n- display a loader view (an `UITableViewCell`) for indicate the next page is loading\n- display a view for errors\n\n## Screenshots\n\n![Example](./Screens/example.gif \"Example View\")\n\n## Usage\n\n### Minimum usage\n\nYou can either use `JTFullTableViewController` which is almost like `UITableViewController`.\nOr 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.\nIf your controller is fullscreen use `JTFullTableViewController` else use `JTTableViewController`.\n\n```swift\nimport JTTableViewController\n\nclass ViewController: JTTableViewController\u003cYourModel\u003e, UITableViewDelegate, UITableViewDataSource {\n    \n    // Used in this example to manage your pagingation\n    private var currentPage = 1\n    \n    // Must be implemented\n    override func jt_tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -\u003e UITableViewCell {\n        let cell = tableView.dequeueReusableCell(withIdentifier: \"cell\")\n        let anInstanceOfYourModel = self.results[indexPath.row]\n        /*\n            whatever you wanna do with `anInstanceOfYourModel` and your `cell`\n        */\n        return cell\n    }\n\n    // Must be implemented\n    override func fetchResults() {\n        super.fetchResults()\n        \n        currentPage = 1\n        \n        // `lastRequestId` is used to avoid problem with parallel requests\n        let lastRequestId = self.lastRequestId\n        \n        YourService.retrieveData(page: currentPage) { (error, results) -\u003e () in\n            if let error = error {\n                self.didFailedToFetchResults(error: error, lastRequestId: lastRequestId)\n                return\n            }\n            self.didFetchResults(results: results, lastRequestId: lastRequestId) {\n                // this block is executed if `lastRequestId` matched with `self.lastRequestId`\n                self.currentPage += 1\n            }\n        }\n    }\n\n    // Must be implemented\n    override func fetchNextResults() {\n        super.fetchNextResults()\n        \n        // `lastRequestId` is used to avoid problem with parallel requests\n        let lastRequestId = self.lastRequestId\n        \n        YourService.retrieveData(page: currentPage) { (error, results) -\u003e () in\n            if let error = error {\n                self.didFailedToFetchResults(error: error, lastRequestId: lastRequestId)\n            }\n            else {\n                self.didFetchNextResults(results: results, lastRequestId: lastRequestId) {\n                    // this block is executed if `lastRequestId` matched with `self.lastRequestId`\n                    self.currentPage += 1\n                }\n            }\n        }\n    }\n    \n}\n```\n\n### Advanced usage\n\n```swift\nimport JTTableViewController\n\nclass ViewController: JTTableViewController\u003cYourModel\u003e, UITableViewDelegate, UITableViewDataSource {\n    \n    // Used in this example to manage your pagingation\n    private var currentPage = 1\n    \n    private let refreshControl = UIRefreshControl()\n    \n    override func viewDidLoad () {\n        super.viewDidLoad()\n        \n        // `nextPageLoaderCell` is an `UITableViewCell`\n        self.nextPageLoaderCell = MyNextPageLoadCell()\n        \n        // `fecthResults` is call 5 cells before `nextPageLoaderCell` become visible\n        self.nextPageLoaderOffset = 5\n        \n        // `noResultsView` is display when `didFetchResults` is called with an `results` empty\n        let noResultsView = NoResultsView()\n        self.noResultsView = noResultsView\n        self.view.addSubview(noResultsView)\n        // something better than frame with Constraints but not relevant here\n        noResultsView.frame = self.view.bounds\n\n        // `noResultsLoadingView` is display when `fetchResults` is called and `results` is empty\n        let noResultsLoadingView = NoResultsLoadingView()\n        self.noResultsLoadingView = noResultsLoadingView\n        self.view.addSubview(noResultsLoadingView)\n        // something better than frame with Constraints but not relevant here\n        noResultsLoadingView.frame = self.view.bounds\n\n        // `errorView` is display when `didFailedToFetchResults` is called\n        let errorView = ErrorView()\n        self.errorView = errorView\n        self.view.addSubview(errorView)\n        // something better than frame with Constraints but not relevant here\n        errorView.frame = self.view.bounds\n        \n        refreshControl.addTarget(self, action: #selector(fetchResults), for: .valueChanged)\n        self.tableView?.addSubview(refreshControl)\n    }\n    \n    override func viewWillAppear(_ animated: Bool) {\n        super.viewWillAppear(animated)\n        fetchResults()\n    }\n    \n    // Must be implemented\n    override func jt_tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -\u003e UITableViewCell {\n        let cell = tableView.dequeueReusableCell(withIdentifier: \"cell\")\n        let anInstanceOfYourModel = self.results[indexPath.row]\n        /*\n            whatever you wanna do with `anInstanceOfYourModel` and your `cell`\n        */\n        return cell\n    }\n\n    // Must be implemented\n    override func fetchResults() {\n        self.resetResults()\n    \n        super.fetchResults()\n        \n        currentPage = 1\n        \n        // `lastRequestId` is used to avoid problem with parallel requests\n        let lastRequestId = self.lastRequestId\n        \n        YourService.retrieveData(page: currentPage) { (error, results) -\u003e () in\n            if let error = error {\n                self.didFailedToFetchResults(error: error, lastRequestId: lastRequestId)\n                return\n            }\n            self.didFetchResults(results: results, lastRequestId: lastRequestId) {\n                // this block is executed if `lastRequestId` matched with `self.lastRequestId`\n                self.currentPage += 1\n            }\n        }\n    }\n\n    // Must be implemented\n    override func fetchNextResults() {\n        super.fetchNextResults()\n        \n        // `lastRequestId` is used to avoid problem with parallel requests\n        let lastRequestId = self.lastRequestId\n        \n        YourService.retrieveData(page: currentPage) { (error, results) -\u003e () in\n            if let error = error {\n                self.didFailedToFetchResults(error: error, lastRequestId: lastRequestId)\n            }\n            else {\n                self.didFetchNextResults(results: results, lastRequestId: lastRequestId) {\n                    // this block is executed if `lastRequestId` matched with `self.lastRequestId`\n                    self.currentPage += 1\n                }\n            }\n        }\n    }\n    \n    override func didEndFetching () {\n        super.didEndFetching()\n        refreshControl.endRefreshing()\n    }\n    \n}\n```\n\nYou have to implement `fetchResults` and `fetchNextResults` methods. They are used to load data (from your web service for example). These methods must call `super`.\n\nOptionaly, you can override `jt_tableView(tableView:heightForRowAt:)` for defining the height of cells.\n\n`fetchResults` is used to retrieve new data (erase all previous data) whereas `fetchNextResults` is used for get more data (the pagination).\n\n`didFetchResults` must be call when `fetchResults` have successfuly retrieve data.\n`didFetchNextResults` must be call when `fetchNextResults` have successfuly retrieve data.\n`didFailedToFetchResults` must be call if `didFetchResults` or `didFetchNextResults` have failed to retrieve data.\n\n`didEndFetching` is called after `didFetchResults`, `didFetchNextResults` or `didFailedToFetchResults`\n\nThe data are stored in `results`. Just use `self.results` to access to them.\nIf you want to remove some elements in `results` you can use `self.unsafeResults`, only in specific case (ex: removing one cell).\n\nThere are some properties you can customize:\n- `nextPageLoaderCell` is the loader use for the pagination, it's a `UITableViewCell`\n- `noResultsView` is the view display when the results get from your service are empty\n- `noResultsLoadingView` is the view display when there is no results and you start fetching new data, used for the first load\n- `errorView` is the view display `didFailedToFetchResults` is called\n- `nextPageLoaderOffset` is the number of cells require before the last cell for calling `fetchNextResults`, by default it's 3\n\nYou can also override some methods:\n- `didEndFetching`\n- `showNoResultsLoadingView`\n- `hideNoResultsLoadingView`\n- `showNoResultsView`\n- `hideNoResultsView`\n- `showErrorView`\n- `hideErrorView`\n\n### Sections\n\n`JTTableViewController` support section management. You just have to override 2 method:\n- `numberOfSections(tableView:)`\n- `jt_tableView(tableView:numberOfRowsInSection:)`\n\n```swift\nimport JTTableViewController\n\nclass ViewController: JTTableViewController\u003c[Int: [YourModel]]\u003e, UITableViewDelegate, UITableViewDataSource {\n\n    @objc(numberOfSectionsInTableView:)\n    override func numberOfSections(in tableView: UITableView) -\u003e Int {\n        return self.results.count\n    }\n\n   override func jt_tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -\u003e Int {\n        return results[section].count\n    }\n\n}\n```\n\n## Subclassing notes\n\nIf you want to subclass `JTTableViewController` or `JTFullTableViewController`, the methods from `UITableViewDelegate` and `UITableViewDataSource` must have an `@objc` annotation.\n\n```swift\nclass XXTableViewController\u003cT\u003e: JTTableViewController\u003cT\u003e {\n    /*\n    ... Here you add whatever you want to add\n    */\n}\n\nclass MyViewController: XXTableViewController\u003cYourModel\u003e {\n\n    // if you don't add `@objc(tableView:didSelectRowAtIndexPath:)` this method is not called\n    @objc(tableView:didSelectRowAtIndexPath:)\n    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {\n        /*\n        ...\n        /*\n    }\n\n}\n```\n\n## Requirements\n\n- iOS 8.0 or higher\n- Swift 3.0\n\n## Author\n\n- [Jonathan VUKOVICH TRIBOUHARET](https://github.com/jonathantribouharet) ([@johnvuko](https://twitter.com/johnvuko))\n\n## License\n\nJTTableViewController is released under the MIT license. See the LICENSE file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnvuko%2Fjttableviewcontroller","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjohnvuko%2Fjttableviewcontroller","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnvuko%2Fjttableviewcontroller/lists"}