LoginSignup
8
6

More than 5 years have passed since last update.

UITableViewのBottomでPullToRefresh(SwipeUpToRefresh?)する方法

Last updated at Posted at 2018-06-04

Swiftで、TableViewとかのScrollViewで、SwipeUpToRefreshする方法のメモ。
ソースコードがあるので、そちらを使って貰えると話が早いと思います。

MEMO: SwipeUpToRefresh とは、上に下から引っ張ったときにRefreshすることを言いたかった。

どんな話か

以下、赤い奴みたいなのがほしい人むけの記事です:
スクリーンショット 2018-10-12 21.09.56.png

環境:

  • Swift4.2
  • Xcode10.0

ソースコード

以下:
Sample Source Code: https://github.com/ykeisuke/sample-pull-to-refresh-at-bottom

解説

手法概要:

  1. スクロールを感知
  2. 一番下の方に行ってれば、Indicatorをチラ見させる
  3. 閾値を超えていればRefresh開始する。(Indicatorは一番下の方に固定しておく

Indicatorの位置はConstraintの変更で対応している。

 UIViewController

ViewController.swift
override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        tableView.delegate = self

tableView.delegate = self で、Delegateを設定しておく

ViewController.swift
extension ViewController: UITableViewDelegate {

    //func scrollViewDidScroll(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {

        // 下部でロードをしていいか、判定する
        let contentSize = scrollView.contentSize.height
        let tableSize = scrollView.frame.size.height - scrollView.contentInset.top - scrollView.contentInset.bottom
        let canLoadFromBottom = contentSize > tableSize

        // Offset
        // 差分を計算して、 `<= -120.0` で、閾値を超えていればrefreshするようにする。
        let currentOffset = scrollView.contentOffset.y
        let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height
        let difference = maximumOffset - currentOffset

        // Indicatorをごにょる。スクロールしている可能性があったり、表示するべきであれば表示する。
        if canLoadFromBottom, self.isLoadingMore == false {
            indicatorFrame.isHidden = false
            var indicatorDifference = difference + indicatorFrame.frame.height
            indicatorDifference = indicatorDifference * CGFloat.init(-1.0)
            // 一番下で固定しておくためなので
            if indicatorDifference > 0 {
                indicatorDifference = 0
            }
            indicatorBottomAlignmentConstraint.constant = indicatorDifference
            indicatorFrame.layoutIfNeeded()
        }
        if difference == 0.0 {
            indicatorFrame.isHidden = true
        }

        // Difference threshold as you like. -120.0 means pulling the cell up 120 points
        if canLoadFromBottom, difference <= -120.0 {

            // Loading中なら、もう一回ロードしないようにする。
            if (self.isLoadingMore == false) {

                // Save the current bottom inset

                // Add 50 points to bottom inset, avoiding it from laying over the refresh control.
                let previousScrollViewBottomInset = scrollView.contentInset.bottom
                scrollView.contentInset.bottom = previousScrollViewBottomInset + 50

                indicator.startAnimating()
                indicatorBottomAlignmentConstraint.constant = 0
                indicatorFrame.layoutIfNeeded()

                self.isLoadingMore = true

                // TODO: ここでローディング中に何かやりたい人はやればよい。↓は終わったら呼び出せば良い

                // loadMoreData function call
                // original:
                // loadMoreDataFunction(){ result in
                DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(5)) {
                    // Reset the bottom inset to its original value
                    scrollView.contentInset.bottom = previousScrollViewBottomInset
                    self.isLoadingMore = false

                    self.indicator.stopAnimating()
                    self.indicatorBottomAlignmentConstraint.constant = -50


                }
            }
        }
    }

}

ほか、メモ

Indicatorを表示したいときは、以下の場所で表示/非表示を操作すればいい。

👇ここで、位置を少し上げて維持させてる。

ViewController.swift
scrollView.contentInset.bottom = previousScrollViewBottomInset + 50

👇で、ロードが終わったとして、位置を戻してる。

ViewController.swift
// Reset the bottom inset to its original value
scrollView.contentInset.bottom = previousScrollViewBottomInset
self.isLoadingMore = false

Refresh処理は、👇のあたりでやればいい。
👇の処理は、「5秒でロードが終わった」という設定で、場所を戻すためのサンプル。
(処理が終わったら位置を元に戻す処理を☝️のように呼び出せばオッケー)

ViewController.swift
// loadMoreDataFunction(){ result in
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(5)) {

参考: https://stackoverflow.com/questions/27190848/how-to-show-pull-to-refresh-element-at-the-bottom-of-the-uitableview-in-swift

以上φ('ᴗ'」)

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6