JTTableViewController
一个带有 tableView 的 ViewController,用于管理 iOS 中的分页和加载器。
安装
使用 CocoaPods,将以下行添加到您的 Podfile。
pod 'JTTableViewController', '~> 2.0'
包含内容
- 避免并行请求问题(您开始两个请求,第一个请求在第二个请求之后完成),我们只想使用最后一个请求
- 轻松管理分页
- 显示第一个加载时的视图(当您的
tableView
为空时) - 显示没有结果的视图
- 显示加载器视图(一个
UITableViewCell
)以指示下一页正在加载 - 显示错误视图
屏幕截图
使用方法
最小化使用
您可以使用类似于 UITableViewController
的 JTFullTableViewController
。或者,您可以从 JTTableViewController
、UITableViewDelegate
、UITableViewDataSource
继承,创建一个 UITableView
,将其分配给 self.tableView
,将其添加到 self.view
中,并为您自己设置 delegate
和 dataSource
。如果您的控制器是全屏,则使用 JTFullTableViewController
,否则使用 JTTableViewController
。
import JTTableViewController
class ViewController: JTTableViewController<YourModel>, UITableViewDelegate, UITableViewDataSource {
// Used in this example to manage your pagingation
private var currentPage = 1
// Must be implemented
override func jt_tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
let anInstanceOfYourModel = self.results[indexPath.row]
/*
whatever you wanna do with `anInstanceOfYourModel` and your `cell`
*/
return cell
}
// Must be implemented
override func fetchResults() {
super.fetchResults()
currentPage = 1
// `lastRequestId` is used to avoid problem with parallel requests
let lastRequestId = self.lastRequestId
YourService.retrieveData(page: currentPage) { (error, results) -> () in
if let 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
override func fetchNextResults() {
super.fetchNextResults()
// `lastRequestId` is used to avoid problem with parallel requests
let lastRequestId = self.lastRequestId
YourService.retrieveData(page: currentPage) { (error, results) -> () in
if let 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
}
}
}
}
}
高级使用
import JTTableViewController
class ViewController: JTTableViewController<YourModel>, UITableViewDelegate, UITableViewDataSource {
// Used in this example to manage your pagingation
private var currentPage = 1
private let refreshControl = UIRefreshControl()
override func 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
let noResultsView = 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
let noResultsLoadingView = 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
let errorView = 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)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
fetchResults()
}
// Must be implemented
override func jt_tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
let anInstanceOfYourModel = self.results[indexPath.row]
/*
whatever you wanna do with `anInstanceOfYourModel` and your `cell`
*/
return cell
}
// Must be implemented
override func fetchResults() {
self.resetResults()
super.fetchResults()
currentPage = 1
// `lastRequestId` is used to avoid problem with parallel requests
let lastRequestId = self.lastRequestId
YourService.retrieveData(page: currentPage) { (error, results) -> () in
if let 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
override func fetchNextResults() {
super.fetchNextResults()
// `lastRequestId` is used to avoid problem with parallel requests
let lastRequestId = self.lastRequestId
YourService.retrieveData(page: currentPage) { (error, results) -> () in
if let 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
}
}
}
}
override func didEndFetching () {
super.didEndFetching()
refreshControl.endRefreshing()
}
}
您必须实现 fetchResults
和 fetchNextResults
方法。它们用于加载数据(例如您的网络服务)。这些方法必须调用 super
。
可选,您可以通过重写 jt_tableView(tableView:heightForRowAt:)
来定义单元格的高度。
fetchResults
用于检索新数据(擦除所有以前的数据),而 fetchNextResults
用于获取更多数据(分页)。
didFetchResults
必须在 fetchResults
成功检索数据时调用。必须调用 didFetchNextResults
,当 fetchNextResults
成功检索数据时。如果在检索数据时失败,则必须调用 didFailedToFetchResults
。
didEndFetching
在 didFetchResults
、didFetchNextResults
或 didFailedToFetchResults
之后被调用。
数据存储在 results
中。只需使用 self.results
来访问它们。如果您想从 results
中删除某些元素,可以使用 self.unsafeResults
,仅在某些特定情况下(例如:删除一个单元格)。
有一些属性您可以自定义
nextPageLoaderCell
是用于分页的加载器,它是一个UITableViewCell
。noResultsView
是当您从服务中获得的结果为空时显示的视图。noResultsLoadingView
是在您开始获取新数据时显示的视图,用于第一次加载。errorView
是当调用didFailedToFetchResults
时显示的视图。nextPageLoaderOffset
是在调用fetchNextResults
之前需要多少个单元格,默认为 3。
您也可以重写一些方法
didEndFetching
showNoResultsLoadingView
hideNoResultsLoadingView
showNoResultsView
hideNoResultsView
showErrorView
hideErrorView
章节
JTTableViewController
支持分区管理。您只需重写两个方法
numberOfSections(tableView:)
jt_tableView(tableView:numberOfRowsInSection:)
import JTTableViewController
class ViewController: JTTableViewController<[Int: [YourModel]]>, UITableViewDelegate, UITableViewDataSource {
@objc(numberOfSectionsInTableView:)
override func numberOfSections(in tableView: UITableView) -> Int {
return self.results.count
}
override func jt_tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return results[section].count
}
}
subclassing notes
如果您想要继承 JTTableViewController
或 JTFullTableViewController
,则必须使用 @objc
注释来自 UITableViewDelegate
和 UITableViewDataSource
的方法。
class XXTableViewController<T>: JTTableViewController<T> {
/*
... Here you add whatever you want to add
*/
}
class MyViewController: 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 或更高版
- Swift 3.0
Author
License
JTTableViewController 是在 MIT 许可证下发布的。有关更多信息,请参阅 LICENSE 文件。