Requestパラメータでpageを指定すれば指定したページの情報を取得できるようなAPIがあるとして、
そのAPIから取得したコンテンツをアプリでページングして表示する実装の備忘録です。
読み込み状態を定義
/// APIの読み込み状況
enum APILoadingStatus {
case initial
case fetching
case loadMore
case full
case error
}
Viewでの実装
final class ViewController: UIViewController {
/// APIの最終取得Page
private var pageOfLastFetched = 1
/// APIの読み込み状況
private var loadStatus: APILoadingStatus = .initial
}
インクリメントして使うPage数と、読み込み状態を保持するプロパティを宣言します。
// APIを使用してコンテンツを取得する
func fetchContents() {
// 読み込み中またはもう次に記事がない場合にはapiを叩かない
if loadStatus == .fetching || loadStatus == .full {
return
}
// loadStatusを読み込み中に変更
loadStatus = .fetching
// 記事一覧を取得するAPIを叩く
repository.getContents(page: pageOfLastFetched) { [weak self] result in
guard let self = self else {
return
}
switch result {
case .success(let contents):
if contents.isEmpty {
// 結果が空だった=もう続きの記事がないのでloadStatusをfullにする
self.loadStatus = .full
return
}
self.pageOfLastFetched.increment() // +1
// 記事取得が終わった段階でloadStatusをloadMoreにする
self.loadStatus = .loadMore
case .failure(let error):
// loadStatusをエラーにする
self.loadStatus = .error
// TODO: Error handling.
print(error)
}
}
}
APIをコールするメソッドを定義します。
成功したらPage数をインクリメントして、最後までFetchしたら読み込み状態を .full
にして以後APIをコールしないようにします。
// MARK: - UIScrollViewDelegate
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// 一定割合スクロールすると次のPageを読み込む
let currentOffsetY = scrollView.contentOffset.y
let maximumOffset = scrollView.contentSize.height - scrollView.frame.height
let distanceToBottom = maximumOffset - currentOffsetY
// 最下部まで500pxを切ったら次のページを取得する
if distanceToBottom < 500 {
fetchContents()
}
}
ページングするタイミングですが、今回は画面最下部500pxを切るところまでスクロールされたらAPIでFecthするようにしました。