[swift]挫折しながら覚えるiOS開発その10 UIRefreshControl(引っ張ってリロード)の追加


[まとめ] 現在開催中のKindleセール情報はこちら

前回は、詳細ページ(WebView)のカスタマイズを行いました。

今回は再びTableViewを使った一覧ページに戻り、TwitterアプリやFacebookアプリでもよく見かける引っ張ってTableViewをリロードする機能(UIRefreshControl)を追加したいと思います。

1. 既存のsetupLinksの改良

その前に、まずは既存のsetupLinksメソッドに少し手を加えます。

現状だとアプリを起動したタイミングで毎回APIにアクセスしてデータを取得するようになっていますが、ランキングの更新は週に1回しかないのでAPIの結果はキャッシュしてよさそうです。

サーバ負荷も減るので、基本的には24時間キャッシュするようにして、必要あれば強制的に更新できるようにしておきます。

以下にsetupLinksのbeforeとafterを載せておきます。

NSUserDefaultsを用いて、jsonの結果と、有効期限のunixtimeを保存することにしました。

あとはキャッシュの有無によって、サーバからデータを取得するかキャッシュからデータを取得するかを出し分けています。

また、引数にforceReloadを追加し、trueの場合は強制更新をかけられるようにしました。

# MasterTableViewController.swift

## before
    func setupLinks() {
        Alamofire.request(.GET, "http://qiita-stock.info/api.json")
            .responseJSON { _, _, data, _ in
                var json = JSON(data!)
                for (i, topic) in json {
                    var links = [Link]()
                    for (k, link) in topic["links"] {
                        links.append(Link(
                            title: link["title"].asString!,
                            username: link["author"].asString!,
                            userIconUrl: link["icon"].asString!,
                            stockCount: link["stock"].asInt!,
                            isNew: link["is_new"].asBool!
                        ))
                    }
                    self.sections.append(topic["send_date"].asString!)
                    self.linkList.append(links)
                }
                self.tableView.reloadData()
        }
    }

## after
    func setupLinks(forceReload: Bool = false) {
        let defaults = NSUserDefaults.standardUserDefaults()
        let cachedJson: AnyObject? = defaults.objectForKey("cachedJson")
        let expireUnixtime: NSTimeInterval = defaults.doubleForKey("expireUnixtime")
        let currentUnixtime = NSDate().timeIntervalSince1970
        // URLでキャッシュされてしまいデータ更新されないことがあったため追加
        // 100秒間でパラメータが切り替わるように設定
        let cacheClearParam = Int(currentUnixtime/100)

        if (expireUnixtime == 0 || currentUnixtime > expireUnixtime || forceReload == true) {
            Alamofire.request(.GET, "http://qiita-stock.info/api.json?t=\(cacheClearParam)")
                .responseJSON { _, _, data, error in

                    if (error != nil) {
                        self.showNetworkAlert()
                        return
                    }

                    self.loadTableCells(data!)

                    defaults.setObject(data, forKey:"cachedJson")
                    // キャッシュ有効期限を24時間に設定
                    defaults.setDouble(currentUnixtime + 60 * 60 * 24, forKey: "expireUnixtime")
                    defaults.synchronize()
            }
        } else {
            if let data: AnyObject = cachedJson {
                self.loadTableCells(data)
            }
        }
    }

    // jsonの内容を元にテーブルを更新
    private func loadTableCells(json: AnyObject) {
        self.linkList.removeAll()
        self.sections.removeAll()

        let json = JSON(json)
        for (i, topic) in json {
            var links = [Link]()
            for (k, link) in topic["links"] {
                links.append(Link(
                    title: link["title"].asString!,
                    url: link["url"].asString!,
                    username: link["author"].asString!,
                    userIconUrl: link["icon"].asString!,
                    stockCount: link["stock"].asInt!,
                    isNew: link["is_new"].asBool!
                    ))
            }
            self.sections.append(topic["send_date"].asString!)
            self.linkList.append(links)
        }

        if let tableVIew = tableView {
            tableView.reloadData()
        }
    }

これでsetupLinksを改良できました。

2. UIRefreshControlの実装

続いてはいよいよ引っ張ってリロードの実装です。

インスタンス変数にrefreshを追加し、viewDidLoad内で設定しています。

引っ張ってリロードが実行されたタイミングでpullToRefreshメソッドが実行されるようにしています。

pullToRefreshメソッドでは、setupLinksをAPI経由の強制読み込みで実行し、refreshのアニメーションを終了させています。

# MasterTableViewController.swift

class MasterTableViewController: BaseViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet weak var tableView: UITableView!

    var linkList = [[Link]]()
    var sections = [String]()
    var refresh = UIRefreshControl()

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.delegate = self
        tableView.dataSource = self

        self.setupLinks()

        // 引っ張って更新
        refresh.addTarget(self, action: "pullToRefresh", forControlEvents:.ValueChanged)
        tableView.addSubview(refresh)
    }

    // 引っ張って更新が実行された際の処理
    func pullToRefresh() {
        // APIから強制読み込み
        self.setupLinks(forceReload: true)
        //更新完了
        refresh.endRefreshing()
    }

これでシミュレータを起動して、TableViewを下に引っ張ってみるとローディングが表示されるようになります。

swift10-1

UIRefreshControl自体の設定は、少しコードを書くだけで実現できるのでお手軽ですね。

最後まで引っ張るとpullToRefreshが呼び出され、ローディングが終了します。

これでTableViewの引っ張ってリロード機能が実装できました。

次回は、ローカルPush機能の実装を行います。

参考

[まとめ] 現在開催中のKindleセール情報はこちら