概要
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 }
}
}
動画
WKWebViewで規約ページの一番下に達したかどうかを判定(動画のページはqiita) pic.twitter.com/0tHKtx4PG7
— takkei (@takkei9) 2018年2月18日
補足
1回一番下まで行ったら後でまた上にスクロールしてもずっと有効したい場合、以下のようにオペレータを追加しましょう。
.filter { $0 }
.take(1)
.bind(to: self.agreeButton.rx.isEnabled)