Automatic UITableView Paging

Episode #309 | 15 minutes | published on November 2, 2017 | Uses Xcode-9.1-beta1, swift-4
Free Video
UITableView can support scrolling through many rows of data, however fetching large amounts of remote data can slow down your app, use up too much memory, and bog down your web server. This is all wasteful if users aren‘t ever going to scroll down that far. In this episode you‘ll learn how to perform automatic UITableView paging using an easy technique.

This episode is a refresh of Episode 8.

Setting Up

    private var beers: [Beer] = []
    private var currentPage = 1
    private var shouldShowLoadingCell = false
override func viewDidLoad() {
    // ...

    refreshControl = UIRefreshControl()
    refreshControl?.addTarget(self, action: #selector(refreshBeers), for: .valueChanged)

    refreshControl?.beginRefreshing()
    loadBeers()
}

Loading the Data

    private func loadBeers(refresh: Bool = false) {
        print("Fetching page \(currentPage)")
        breweryDBClient.fetchBeers(page: currentPage, styleId: 3) { page in
            DispatchQueue.main.async {

                if refresh {
                    self.beers = page.data
                } else {
                    for beer in page.data {
                        if !self.beers.contains(beer) {
                            self.beers.append(beer)
                        }
                    }   
                }

                self.shouldShowLoadingCell = page.currentPage < page.numberOfPages

                self.refreshControl?.endRefreshing()
                self.tableView.reloadData()
            }
        }
    }

Fetching the next page

private func fetchNextPage() {
    currentPage += 1
    loadBeers()
}

Showing the Loading Cell

If we need to show a loading cell we need to add 1 to the cell count:

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        let count = beers.count
        return shouldShowLoadingCell ? count + 1 : count
    }

Then return a cell for the loading index path:

    private func isLoadingIndexPath(_ indexPath: IndexPath) -> Bool {
        guard shouldShowLoadingCell else { return false }
        return indexPath.row == self.beers.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        if isLoadingIndexPath(indexPath) {
            return LoadingCell(style: .default, reuseIdentifier: "loading")
        } else {
            //...
        }
    }

Then we need to trigger the next page load when the cell is displayed...

    override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        guard isLoadingIndexPath(indexPath) else { return }
        fetchNextPage()
    }

Refreshing the Data

@objc
private func refreshBeers() {
    currentPage = 1
    loadBeers(refresh: true)
}
blog comments powered by Disqus