11
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Swift 3.2以降】WKWebView の estimatedProgress の値監視に新しい Block ベースの KVO API を使ってみる

Posted at

はじめに

先日,WKWebViewのestimatedProgressが1.0になったら
自然にUIProgressViewが非表示になるように見せる
1 という
記事を投稿しました。挙動の違いでモヤモヤ感は残ったものの
fatal ではないのでそのままアプリに移植し,運用していました。

今日,急に思い立って移植したプロジェクトに
Swift Lint を導入し,リファクタリングをしていたら
下記の warning が出ていました。

warning: Block Based KVO Violation: Prefer the new block based KVO API with keypaths when using Swift 3.2 or later. (block_based_kvo)

Swift 3.2 以降では keypaths を持つ新しいブロックベースの KVO APIを推奨します。

とのことなので Swift 4 用に書き直して見ました。

実装

環境

  • Xcode 9.2
  • Swift 4 (API 自体は Swift 3.2 以上)
  • macOS 10.12.6 以降

サンプルコード

以前の記事のサンプルに変更加えました。
サンプルは下記になります。動作的には同じに見えます。

サンプルコード(GitHub)

これまでの実装

WKWebViewestimatedProgress の値の変化を監視して
UIProgressView に反映する実装部分を抜き出すと下記になります。
一般的な KVO の形で keyPath の文字列比較や
オブザーバの生成・削除などいろいろやることがありました。

HogeViewController.swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    // estimatedProgress を監視
    self.webView.addObserver(self, forKeyPath: ObserveKey.estimatedProgress, options: .new, context: nil)
}

deinit {
    self.webView.navigationDelegate = nil
    self.webView.uiDelegate = nil
    // オブザーバを削除
    self.webView.removeObserver(self, forKeyPath: ObserveKey.estimatedProgress, context: nil)
}

func setUpUI() {
    self.progressView = UIProgressView(frame: CGRect(x: 0.0,
                                                     y: (self.navigationController?.navigationBar.frame.size.height)! - 3.0,
                                                     width: self.view.frame.size.width,
                                                     height: 3.0))
    self.progressView.progressViewStyle = .bar
    self.progressView.progressTintColor = .red
    self.navigationController?.navigationBar.addSubview(self.progressView)
}

// MARK: - KVO
/// WebViewの読み込み時のProgress KVO
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {

    if (keyPath == ObserveKey.estimatedProgress) {
        self.progressView.alpha = 1.0
        // estimatedProgressが変更されたときにプログレスバーの値を変更
        self.progressView.setProgress(Float(self.webView.estimatedProgress), animated: true)

        if (self.webView.estimatedProgress >= 1.0) {
            UIView.animate(withDuration: 0.3,
                           delay: 0.3,
                           options: [.curveEaseOut],
                           animations: { [weak self] in
                            self?.progressView.alpha = 0.0
                }, completion: { _ in
                    self.progressView.setProgress(0.0, animated: false)
            })
        }
    }
}

新しい KVO API を使ってみる

新しい Block ベースの KVO API について
warning の文章をそのまま検索してみたところ,
Swift Lint の issue2 に上がっていたので,参考に書き直しました。
下記のようになりました。

HogeViewController.swift
// nil を書くと Redundant Optional Initialization Violation で冗長
var observeLoading: NSKeyValueObservation?

func setUpUI() {
    self.progressView = UIProgressView(frame: CGRect(x: 0.0,
                                                     y: (self.navigationController?.navigationBar.frame.size.height)! - 3.0,
                                                     width: self.view.frame.size.width,
                                                     height: 3.0))
    self.progressView.progressViewStyle = .bar
    self.navigationController?.navigationBar.addSubview(self.progressView)

    // Swift 3.2 以降の KVO
    self.observeKeysFowWebView()
}


func observeKeysFowWebView() {

    // WKWebView の estimatedProgress の値監視(中の処理は同じ)
    self.observeEstimatedProgress = self.webView.observe(\.estimatedProgress, options: [.new], changeHandler: { (webView, change) in
        self.progressView.alpha = 1.0
        // estimatedProgressが変更されたときにプログレスバーの値を変更
        self.progressView.setProgress(Float(change.newValue!), animated: true)

        if (self.webView.estimatedProgress >= 1.0) {
            UIView.animate(withDuration: 0.3,
                           delay: 0.3,
                           options: [.curveEaseOut],
                           animations: { [weak self] in
                            self?.progressView.alpha = 0.0
                }, completion: { _ in
                    self.progressView.setProgress(0.0, animated: false)
            })
        }
    })
}

WKWebView だと他に titleURLloading がありますね。
同様にすれば対応可能だと思います。
今回は新しい値だけをみたのですが,
色々対応するなら options[.new, .old] の方がいいのかな。

おわりに

今回は,Swift 3.2 以降で推奨になった Block ベースの KVO について
WKWebViewestimatedProgress の値監視を例に記事を書きました。
Swift 3.2 から推奨ということでそれ以下のバージョンに対応するなら
これまで通りの KVO でプロパティ監視することになります。

Swift Lint 導入して警告や warning を修正するにあたって,
気づくことも多く,うまく付き合っていけたらなと思います。
ご覧いただきありがとうございました。
理解を深めたいのでコード間違いやご意見ありましたらよろしくお願いいたします。

参考

  1. WKWebViewのestimatedProgressが1.0になったら自然にUIProgressViewが非表示になるように見せる

  2. [Rule Request] Prefer new block based KVO API in Swift 3.2 or 4 #1714

11
9
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
11
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?