TableViewで一番下まで行ったら次のページをロードする

この前、とある課題でニュースリーダーアプリ的なのを作った際、TableViewで一番下まで行ったら次のページをロードするのを実装しようとしたら、割とごちゃっとしてたので自分なりに整理しておきます。

インジケータが乗ったxibファイルを作る

下までスクロールしたときにくるくるしてるやつですね。
これをxibファイル作ってセルに配置して、TableViewにフッターとして埋め込みます。

// ArticleListViewController.swift

 override func viewDidLoad() {
        super.viewDidLoad()

        self.tableView.register(R.nib.loadingCell)
        let footerCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: R.reuseIdentifier.loadingCell.identifier)!
        (footerCell as! LoadingCell).startAnimation()
        let footerView: UIView = footerCell.contentView
        tableView.tableFooterView = footerView
}

一番下にたどりつく前に次のapiを叩く

一番下までスクロール仕切らないところでAPI叩いて、下までたどり着くときにちょうどParseが終わってるようにします。
(つまりさっきのインジケータが日の目を見るのはほんの一瞬)

// ArticleListViewController.swift

func scrollViewDidScroll(_ scrollView: UIScrollView) {

        let currentOffsetY = scrollView.contentOffset.y
        let maximumOffset = scrollView.contentSize.height - scrollView.frame.height
        let distanceToBottom = maximumOffset - currentOffsetY
        if distanceToBottom < 500 && tableView.isDragging {
            self.viewModel?.search()
        }
    }

リクエスト調整

このままだと一回下までスクロールしたらそのままずっとAPI叩いちゃうので、Parseが終わるまでは次のリクエスト送らないように、以下のようなStatusを作ります。

enum LoadStatus {
    case initial
    case fetching
    case full
}

最初は.initialにしておいて、非同期処理の中で状態を設定し直してあげます。

// ArticleListViewModel.swift

func search() {
        
        guard loadStatus == .initial else {
            return
        }
        self.loadStatus = .fetching
        self.shared.sync(query: self.searchWord, page: self.page)
            .subscribe { [weak self] result in
                switch result {
                case .success(let data):
                    self?.articles.append(contentsOf: data.articles)
                    if data.articles.count != 0 {
                        self?.refreshToggle.accept(())
                        self?.page += 1
                        self?.loadStatus = .initial
                    } else {
                        self?.loadStatus = .full
                    }
                case .error(let error):
                    print(error)
                    self?.loadStatus = .initial
                }
            }.disposed(by: disposeBag)
    }

QiitaのAPIだと、一回のリクエストでは最大100件までしか取得できないので、pageって変数に先に叩いたページ保存しておいて、一回リクエエスト叩くごとに増やしていくことで順番に取得していくことができます。

ソースコードの全貌載せておきます。

github.com

参考にした記事はこちら↓

qiita.com

少なくとも私がやった感じQiitaのAPIって割とすぐに403(リクエスト送りすぎ)って返って来ちゃうのでスクロールし過ぎには注意が要りそうです、、、汗

何か気になる所ありましたら是非教えて下さい〜!!

ブログ始めます

自己紹介

はじめまして! お茶の水女子大学大学院修士1年のあおいというものです。

専攻は情報ではなく数学ですが、勉強するに連れて数学って実際どこに活きてるの?数学だけやってて将来大丈夫なの?っていう不安を持つように。

いろいろ考えた結果、数学はITの世界で活きているに違いないという考えに至り(安直)インターンでプログラミングをはじめました。

やってみると開発が楽しすぎたので、現在エンジニア目指して就活してます。

このブログについて

  • 技術的なことに関する備忘録
  • インターンとか就活に関するメモ
  • その他独り言

といった感じで書いていこうと思います。

ちなみにこのブログを書こうというきっかけをくれたのは、私の就活に関する唐突な質問にめちゃ優しく答えてくれたややさんでした↓
ありがとうございます!

blog.yayawatanabe.net

まだまだ開発期間が短いので初歩的なこと、わからないことが多いと思いますが、
何卒よろしくお願いします!