try! Swift2日目でrx_reachedBottomが気になったので作ってみた

Thursday, March 3, 2016

相変わらず、有志の方が書き起こしてくださっているブログやハッシュタグから情報を取り寄せています。どうも。
その中で、 ishkawa さんが発表したスライドの中に、rx_reachedBottomなるものが出てきて、調べてみたらどうやらRxSwiftで用意されているものじゃなかったっぽいので、予想して書いてみました。
執筆時点ではまだサンプルコードが公開されていないので、あくまでも予測なので、正式(?)なものが公開されたら追記します。

注意 ishkawaさんがサンプルプロジェクトを公開していたので確認したところ、 見事に 予想が外れたので、自分の駄作を残しつつ、追記します。

おそらく名前からして、UIScrollView(やUITableView,UICollectionView)が下端までスクロールしたかどうかをObserveすることができるようになると思われます。
RxSwift(RxCocoa)には、UIScrollViewextension としてrx_contentOffsetが用意されているのでこれを利用して、

  • CGPoint → Bool

となるように作ってみます。

こんな感じかなぁと。
これが仮に間違ってたりちょっと違うよ〜ってなってても、自分で考えて書いてみるのは楽しい…!
プレゼン(スライド)の例では、これで下端までスクロールしたかを判定して、下端まできていたらAPI叩いて追加読み込みしてってのをやっているみたいですね。

RxSwift、もっと使いこなせるようになりたい。

  • pod try RxSwift

絶対これやる。

追記


本物のrx_reachedBottomは、以下のように実装されていました。

import UIKit
import RxSwift

extension UIScrollView {
    var rx_reachedBottom: Observable<Void> {
        return rx_contentOffset
            .flatMap { [weak self] contentOffset -> Observable<Void> in
                guard let scrollView = self else {
                    return Observable.empty()
                }

                let visibleHeight = scrollView.frame.height - scrollView.contentInset.top - scrollView.contentInset.bottom
                let y = contentOffset.y + scrollView.contentInset.top
                let threshold = max(0.0, scrollView.contentSize.height - visibleHeight)

                return y > threshold ? Observable.just() : Observable.empty()
            }
    }
}

どうやら、Observableではなく、 Observable で返しているみたいです。
あと私のへっぽこと違ってしっかりinsetとか、値のboundary判定しています
下端まで到達した場合には、 Observable.just() を、そうでなければ Observable.empty() を返しています。
これを用いて、まず

tableView.rx_reachedBottom
           .bindTo(viewModel.loadNextPageTrigger)
           .addDisposableTo(disposeBag)

と、viewModel.loadNextPageTriggerにbindして、

nextPageRequest = [
                    Observable.never().takeUntil(loadNextPageTrigger), // (1)
                    Observable.of(baseRequest.requestWithPage(previousPage + 1)) //(2)
                ]
                .concat()
                .shareReplay(1)

Observable.never()に対してtakeUntilでloadNextPageTriggerが発火する(って表現でいいのかな?)の待つ、つまり先ほどのrx_reachedBottomでObservable.just()が返ってきたら待って、返ってきたらObservable.of(baseRequest.requestWithPage(previousPage + 1))でAPIを叩きにいって追加読み込みをするといった形(のようです。)
うまくconcatを使って、上記コメント中に振った番号、 (1)→(2)となるようにobservableをマージしてってしてるみたいです。

難しいけどしっかり噛み砕いて自分も扱えるようにしたいと思います。justとかneverとかtakeuntilとかshareReplayとか、ofとかもう未知の領域です。笑

techSwiftRxSwifttry! Swift

ReactiveXの処理が図で分かる「RxMarbles」

少しずつ変えていく