JTTableViewController 2.1.4

JTTableViewController 2.1.4

测试已测试
Lang语言 SwiftSwift
许可证 MIT
发布上次发布2019年1月
SPM支持 SPM

Jonathan Vukovich Tribouharet 维护。




  • 作者:
  • Jonathan VUKOVICH TRIBOUHARET

JTTableViewController

CI Status Version License Platform

一个带有 tableView 的 ViewController,用于管理 iOS 中的分页和加载器。

安装

使用 CocoaPods,将以下行添加到您的 Podfile。

pod 'JTTableViewController', '~> 2.0'

包含内容

  • 避免并行请求问题(您开始两个请求,第一个请求在第二个请求之后完成),我们只想使用最后一个请求
  • 轻松管理分页
  • 显示第一个加载时的视图(当您的 tableView 为空时)
  • 显示没有结果的视图
  • 显示加载器视图(一个 UITableViewCell)以指示下一页正在加载
  • 显示错误视图

屏幕截图

Example

使用方法

最小化使用

您可以使用类似于 UITableViewControllerJTFullTableViewController。或者,您可以从 JTTableViewControllerUITableViewDelegateUITableViewDataSource 继承,创建一个 UITableView,将其分配给 self.tableView,将其添加到 self.view 中,并为您自己设置 delegatedataSource。如果您的控制器是全屏,则使用 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()
    }
    
}

您必须实现 fetchResultsfetchNextResults 方法。它们用于加载数据(例如您的网络服务)。这些方法必须调用 super

可选,您可以通过重写 jt_tableView(tableView:heightForRowAt:) 来定义单元格的高度。

fetchResults 用于检索新数据(擦除所有以前的数据),而 fetchNextResults 用于获取更多数据(分页)。

didFetchResults 必须在 fetchResults 成功检索数据时调用。必须调用 didFetchNextResults,当 fetchNextResults 成功检索数据时。如果在检索数据时失败,则必须调用 didFailedToFetchResults

didEndFetchingdidFetchResultsdidFetchNextResultsdidFailedToFetchResults 之后被调用。

数据存储在 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

如果您想要继承 JTTableViewControllerJTFullTableViewController,则必须使用 @objc 注释来自 UITableViewDelegateUITableViewDataSource 的方法。

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 文件。