LoginSignup
6
6

More than 5 years have passed since last update.

[WKWebView][RxSwift] 規約ページの一番下にスクロールした時にネイティブ側の同意ボタンを有効にする

Last updated at Posted at 2018-02-18

概要

 webで公開されている規約ページをWKWebViewで読み込み、ページの一番下までスクロールされたことをアプリでキャッチして同意ボタンを有効にします。
(特に受諾開発の案件でよくあるのかなーと思います)

 この仕様を振られてちょっと困ったのですが、WkWebViewのプロパティでUIScrollViewが公開されているのでdidEndDecelerating()でスクロール位置を判定できるのでした。
https://developer.apple.com/documentation/webkit/wkwebview/1614784-scrollview

 更に、読み込み途中のページでスクロール位置を判定されてはいけないので、estimatedProgressをKVOで監視してロードの判定も行います(無理矢理感あります。他に何かいい方法がありそうです)。

 「ロード完了」かつ「スクロール位置が底」ならば同意ボタンを有効・そうでなければ無効とします。

実装


import UIKit
import RxSwift
import WebKit

class ViewController: UIViewController {


    @IBOutlet weak var webView: WKWebView!
    @IBOutlet weak var agreeButton: UIButton!

    let agreementUrl = URL(string: "https://qiita.com/terms")

    let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        agreeButton.isEnabled = false
        bindScrollAgreement()

        let urlRequest = URLRequest(url: self.agreementUrl!)
        self.webView.load(urlRequest)
    }

    private func bindScrollAgreement() {

        let margin: CGFloat = 5 // 底かどうかのマージン

        Observable.combineLatest(self.webView.rx.isLoadCompleted,
                                 self.webView.scrollView.rx.didEndDecelerating) {(isLoadCompleted, _) -> Bool in

                                    let bottomEdge = self.webView.scrollView.contentOffset.y
                                                        + self.webView.scrollView.frame.size.height
                                                        + margin

                                    let position = bottomEdge - self.webView.scrollView.contentSize.height
                                    let isBottom = position > 0

                                    return isLoadCompleted && isBottom
            }
            .bind(to: self.agreeButton.rx.isEnabled)
            .disposed(by: disposeBag)
    }
}

extension Reactive where Base: WKWebView {

    /// ロード完了かどうか
    public var isLoadCompleted: Observable<Bool> {
        return self.observe(Double.self, "estimatedProgress")
            .throttle(0.1, scheduler: MainScheduler.instance)
            .map { $0 == 1.0 }
    }
}

動画

補足

1回一番下まで行ったら後でまた上にスクロールしてもずっと有効したい場合、以下のようにオペレータを追加しましょう。

    .filter { $0 }
    .take(1)
    .bind(to: self.agreeButton.rx.isEnabled)
6
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
6
6