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


## はじめに
業務で `UIWebView` から `WKWebView` の移行を今更ながらやっております。
個人アプリだとまず `WKWebView` とか `SFSafariViewController` などを
選ぶわけですが，昔からのアプリだとまだ `UIWebView` だったり普通にありますよね・・・

`UIWebView` の方は読み込み開始時に `UIActivityIndicatorView` を出して，
読み込み完了時に消す(ぐるぐるのやつ)という実装やっていましたが，
`WKWebView` は `estimatedProgress` という読み込み具合の値が取れるので，
その値を見て `NavigationBar` 部分に `UIProgressView` を表示するようにしました。
当時の実装だと 0.9 あたりで止まってそのあと非表示になるような感じだったので，
`NavigationBar` の右端まで行ってるように見せたかったのでその修正をしました。

## WKWebView の実装

今回の実装は GitHub にもあげましたので
気になる方がいらっしゃいましたらご覧下さい。

[サンプルプロジェクト](https://github.com/MilanistaDev/WKWebViewProgress)


まずは `WKWebView` を普通に実装します。
iOS 8 以降はこちらを使うように推奨されています。
もうほとんど対応がなくなったかもですが，
iOS 7 に対応する場合は別に `UIWebView` の実装も必要です。

Xcode 9 から `WKWebView` の `addSubView` 周りののコードで書かなくて
良くなったので少しコード減らせるかもしれないですね。(ほんの少しだけ)
せっかくなのでサンプル実装では IB を使ってみます。

![Xcode9_WKWebView.png](https://qiita-image-store.s3.amazonaws.com/0/88266/ee33b050-aabe-3c5e-2a8b-38801a7c7fec.png "Xcode9_WKWebView.png")

コードは最低限書きます。

```Swift:ViewController.swift
import WebKit

class ViewController: UIViewController {

    @IBOutlet weak var wkWebView: WKWebView!
    var progressView = UIProgressView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // NavigationBar の下にUIprogressViewをBar形式で配置
        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)
        
        // KVO 監視
        self.wkWebView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
        self.wkWebView.addObserver(self, forKeyPath: "loading", options: .new, context: nil)

        // URLリクエスト
        let url = URL(string: "https://www.yumemi.co.jp")
        self.wkWebView.load(URLRequest(url: url!, cachePolicy: .reloadRevalidatingCacheData, timeoutInterval: 10.0))
    }

	deinit {
        self.wkWebView.removeObserver(self, forKeyPath: "estimatedProgress", context: nil)
        self.wkWebView.removeObserver(self, forKeyPath: "loading", context: nil)
    }
    
    /// 監視しているWKWebViewのestimatedProgressの値をUIProgressViewに反映
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    	// ここでUIProgressViewに値を入れるコードを書く
    }
}
```

## 最初にやったこと

`loading` が始まったら `0.1` をセットし，
`estimatedProgress` の値を `UIProgressView` に反映させつつ，
`loading` が終わったら `0.0` にするという実装。
実行すると下記のようになる。

![first.gif](https://qiita-image-store.s3.amazonaws.com/0/88266/533b9416-93f7-ebf0-6476-333c3e730236.gif "first.gif")


`0.9` あたりで止まって消えるように見える。`1.0` まで行ってほしい・・・

```Swift:ViewController.swift
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

    if (keyPath == "estimatedProgress") {
        // estimatedProgressが変更されたときにプログレスバーの値を変更
        self.progressView.setProgress(Float(self.commonWKWebView.estimatedProgress), animated: true)
    } else if (keyPath == "loading") {
        UIApplication.shared.isNetworkActivityIndicatorVisible = self.commonWKWebView.isLoading
        if (self.commonWKWebView.isLoading) {
            self.progressView.setProgress(0.1, animated: true)
        }else{
            // 読み込みが終わったら0.0をセット
            self.progressView.setProgress(0.0, animated: false)
        }
    }
}
```

## 2番目にやったこと

`0.0` をセットした `loading` 部分が冗長だったのでは？ということで
`else if` 部分をごっそり省く。実行すると下記のようになる。

![second.gif](https://qiita-image-store.s3.amazonaws.com/0/88266/8c850ccc-f574-5d97-a178-7d5ce5ee4197.gif "second.gif")


ですよねーって感じ。`1.0` にはなるんだけど `UIProgressView` は残ったまま。
ここで見えなくなってほしい。

```Swift:ViewController.swift
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

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

## 最後にやったこと

やはりアニメーションしかないのか・・・
`UIView` のアニメーションを使って `alpha` 値をいじる。
`isHidden` でも良さそうだったけど変わらずだった。
`0.9` ぐらいで消える感じ。

![final.gif](https://qiita-image-store.s3.amazonaws.com/0/88266/d36c0ac2-2a54-5069-0a92-22f42011c0e7.gif "final.gif")



これだと望んだ結果となった。

```Swift:ViewController.swift
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

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

        // estimatedProgressが1.0になったらアニメーションを使って非表示にしアニメーション完了時0.0をセットする
        if (self.wkWebView.estimatedProgress >= 1.0) {
            UIView.animate(withDuration: 0.3,
                           delay: 0.3,
                           options: [.curveEaseOut],
                           animations: { [weak self] in
                            self?.progressView.alpha = 0.0
                }, completion: {
                    (finished : Bool) in
                    self.progressView.setProgress(0.0, animated: false)
            })
        }
    }
}
```

## おわりに

今回は `WKWebView` の `estimatedProgress` の値が `1.0` になったら
`UIProgressView` が非表示になってくれる実装について書きました。
デフォルトでこの動作になってくれればいいのになーと感じました。
細かいところですが逆にこういうところにこだわっていきたいです。

他にいい方法があったら教えて頂けたら嬉しいです！
ご覧いただきましてありがとうございました。

## 追記

GIF 撮ったときの動作は Deployment Target 9.0 以上のプロジェクトで，
`WKWebView` をコードで生成した場合のときのものだったのですが，
その後いろいろ確認していたところ，
IB で `WKWebView` を配置した場合は，最初の方法でもうまく行っているように見えました。

コードで生成した場合と IB で `WKWebView` を配置した場合とで差があるようで悩ましいです。
Xcode 9 の IB で `WKWebView` 配置する際は Deployment Target 11.0 以上じゃないと
警告が出るので iOS 11 に最適化されてるって感じなのでしょうか・・・

![warning.png](https://qiita-image-store.s3.amazonaws.com/0/88266/5bf6e7d8-9add-408e-0a09-972380538624.png "warning.png")

iOS 11 からのプロジェクトってまだなかなかないのでしょうけど。
詳しい方いらっしゃったらご教授お願いいたします。