21
11

More than 5 years have passed since last update.

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

Posted at

はじめに

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

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

WKWebView の実装

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

サンプルプロジェクト

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

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

Xcode9_WKWebView.png

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

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

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

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

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

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

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

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)
            })
        }
    }
}

おわりに

今回は WKWebViewestimatedProgress の値が 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

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

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