iOS アプリにおけるスクロールビューの画面更新について (UIRefreshControl)

iOS アプリにおけるスクロールビューの画面更新について (UIRefreshControl)

iOS アプリ

スマートフォンの牽引役であった iPhone 3G が発売されてから既に10年以上経過しており、今では生活の一部になっています。これまで様々な iOS アプリがリリースされましたが、アプリの UI についても年々進化しています。主には Apple が推奨した方法が一般に展開されていることが多いですが、多くの開発者が利用している実装を Apple が標準として取り込むこともあります。

今回紹介する「スクロールビューにおいて画面更新」についても、多くの開発者が使っている UI を Apple が標準として採用したものになります。
具体的には、画面に表示されている情報を最新にするために、画面を下にスワイプするとインジケータが表示され、画面の情報が最新になるとインジケータが消えるものになります。

一昔前の実装方法

iOS9 までは、自前で用意したりサードパーティ製のプラグインを利用したりしていました。自前で用意する場合、スクロールビューではコンテントビューの上部にインジケータ用の余白を持たせる実装を行ったり、テーブルビューでは最上部のセルにインジケータのみを設置し2つめのセルからを表示するような実装を行っていました。そのため、これらの制御が必要であり大きな手間になっていました。

現在の実装方法

iOS10 より UIRefreshControl を利用できるようになりました。これは UIScrollView, UITableView, UICollectionView で利用することができます。

上記コントローラには refreshControl プロパティが用意されているので、そこに UIRefreshControl クラスのインスタンスを渡すだけで利用できます。

また UIRefreshControl クラスのインスタンスに、インジケータが表示されたときの実行するメソッドを addTarget メソッドで追加します。

実際には、インジケータが表示されている間に何かを処理し、その処理が終わった後にインジケータを消すでしょう。そのときは UIRefreshControl クラスのインスタンスの endRefreshing() メソッドを呼び出せば大丈夫です。

具体的には次のようなソースコードになります。下記は UITableViewController クラスで利用しているので、先の説明と若干異なっていますが、基本的には同じ処理の流れになります。

import UIKit

class ViewController: UITableViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //スクロールビューを下にスワイプした時の処理(インジケータ表示)
        refreshControl = UIRefreshControl()
        refreshControl?.addTarget(self, action: #selector(refresh), for: .valueChanged)
    }

    @objc func refresh() {
        //WebAPIを呼び出す(ここでは仮に2秒後に dataLoaded() を呼び出す)
        DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
            self.dataLoaded()
        }
    }

    //WebAPIからレスポンスを受け取った後に呼び出すメソッド
    func dataLoaded() {
        //インジケータを隠す
        refreshControl?.endRefreshing()
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell") ?? UITableViewCell(style: .default, reuseIdentifier: "cell")
        cell.textLabel?.text = String(indexPath.row)
        return cell
    }
}


まとめ

iOS10 以前に比べると圧倒的にインジケータの表示が簡単になりましたね。久しぶりにネイティブでインジケータを出そうと思ったら、余りにも簡単に出すことができて感動してしまいました。このように開発者にとっても開発しやすくなることはいいことですね。
» エンジニア登録はこちら