この記事は 株式会社アイスタイルアドベントカレンダー の21日目の記事です。
アイスタイルで主にiOS開発を行っています @mishimay です。
Web API と連携したアプリを開発をしていると、tableViewを一番下までスクロールしたら続きのデータを取得して表示、というUIをほとんどの場合で作ると思います。
よくある挙動にも関わらず、これを実現するライブラリは意外と少ないのではないでしょうか。
そこで今回、追加読み込みするtableViewで共通して行う処理をまとめたTableViewControllerを作り、オープンソースとして公開してみました。
アドベントカレンダーといういい機会をもらいましたのでご紹介しようと思います。
リポジトリ
LoadMoreTableViewController
https://github.com/istyle-inc/LoadMoreTableViewController
環境
iOS 8 以上
Swift 2.1
特徴
- 実装の手軽さ
- 最低限必要なことは、表示するcellの用意、2つのクロージャの実装だけです。
- 動きのなめらかさ
- 追加読み込みするときにスクロールが勝手にジャンプする現象を抑えています。
- 動きについては今後もより良くしていきたいです。
- リトライボタン
- データの読み込みに失敗した時、追加読み込みの Activity Indicator の代わりにリトライボタンを表示できます。
- リトライボタンが押されると続きのデータ取得を試みます。
- リトライボタンのテキスト、画像はカスタマイズ可能です。
インストール
CocoaPodsから行ってください。
pod "LoadMoreTableViewController"
基本的な使い方
Cellを用意する
tableViewに表示するCellを用意します。
普段、UITableViewControllerにCellを用意するのと同じ作業です。
ただ注意点として、Cellのidentifierは、LoadMoreTableViewControllerのプロパティ public var cellReuseIdentifier
と合わせてください。
デフォルトの値は "Cell"
です。
このプロパティは変更可能です。
また、AutoLayoutを使用することでCellの高さがCellの中身に合わせて自動で調整されます。
Xibファイルを用意して registerNib
したり、Storyboard上に用意したりしてください。
- Xibファイルを用意する方法
- Xibファイルに Table View Cell を配置
- LoadMoreTableViewControllerのtableViewに
registerNib:forCellReuseIdentifier:
でXibファイルを登録
- Storyboard上に用意する方法
- LoadMoreTableViewControllerを継承した Table View Controller に Table View Cell を配置
- CellにIdentifierを設定
クロージャの設定
以下の2つのクロージャを設定してください。
-
public var fetchSourceObjects: (completion: (sourceObjects: [AnyObject], hasNext: Bool) -> ()) -> ()
- このクロージャの中で新しいデータを取得します。
- データが取得できたら、
completion
クロージャを呼んで以下の情報を返してください。- 新しく取得したオブジェクトの配列 (
sourceObjects:
). - 次の読込みが存在するか (
hasNext:
).
- 新しく取得したオブジェクトの配列 (
-
public var configureCell: (cell: UITableViewCell, row: Int) -> UITableViewCell
- Cellの設定をして返します。
- Cellの型は、上で用意したCellの型と同じなのでキャストして使用してください。
データはLoadMoreTableViewControllerのプロパティ sourceObjects
配列に溜まっていきますので、これを見てoffsetを指定して新たにデータ取得したり、Cellを設定したりしてください。
実装例
import LoadMoreTableViewController
class MyTableViewController: LoadMoreTableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerNib(UINib(nibName: "StandardCell", bundle: nil), forCellReuseIdentifier: cellReuseIdentifier)
fetchSourceObjects = { [weak self] completion in
let offset = self?.sourceObjects.count ?? 0
self?.request(offset: offset) { products in
completion(sourceObjects: products, hasNext: true)
}
}
configureCell = { [weak self] cell, row in
if let cell = cell as? StandardCell, let product = self?.sourceObjects[row] as? Product {
cell.titleLabel.text = product.title
}
return cell
}
}
}
githubのプロジェクト にはExampleも用意していますのでそちらも参考にしてみてください。
さらに詳しい使い方
データの管理について
取得したデータは public var sourceObjects: [AnyObject]
配列に追加されていきます。
このプロパティは直接アクセスしたり操作したりすることができます。
データのリフレッシュ
初めからデータを取得し直したいときは public func refreshData(immediately immediately: Bool)
メソッドを使用してください。
- immediately: true
- immediately引数をtrueにすると、直ちにtableViewが空になり activity indicator が上部にされ、データの取得が始まります。
- immediately: false
- immediately引数をfalseにすると、データを取得した後にtableViewが更新されます。
- つまり、activity indicator が上部に表示されることを防ぎます。
- UIRefreshControlを使用してUIRefreshControlの activity indicator を表示してデータ更新したいときに使用してください。
リトライボタンについて
表示
-
public func showRetryButton()
- activity indicator をリトライボタンに変えます。
- リトライボタンがタップされると、続きのデータ取得が試みられます。
スタイル変更
staticプロパティなので、一回の設定ですべてのLoadMoreTableViewControllerオブジェクトに適応されます。
-
public static var retryText: String?
- リトライボタンの文言を変更します。
-
public static var retryImage: UIImage?
- リトライボタンの画像を設定します。
イベント
-
public var didSelectRow: (Int -> ())?
- Cellが選択されたら呼ばれるクロージャです。
注意点
LoadMoreTableViewControllerはUITableViewControllerを継承しており、UITableViewDataSourceやUITableViewDelegate、UIScrollViewDelegateのメソッドを実装しています。
LoadMoreTableViewControllerのサブクラスでこれらプロトコルのメソッドを override
して実装したい場合は、super
の同じメソッドを呼び出すようにしてください。
今後やりたいこと
- ジェネリクス
-
sourceObjects
配列の要素やconfigureCell()
で受け取るcellの型をジェネリックにしたいです。 - ただ現状、ジェネリックなクラスはStoryboard上で使うとコンパイルエラーになってしまうので今回は見送っています。
-
- カスタマイズ性向上
- さまざまな状況で使えるようにカスタマイズ性を上げていければと思います。
おわりに
今までは画面ごとに追加読み込みの処理を書いてしまっていたのですが、このLoadMoreTableViewControllerを使うことでコード量が少なく、見やすくなり、楽に開発を進められるようになりました。
簡単に使えるはずなので興味を持たれましたらぜひ触ってみてください。