はじめに
業務で UIWebView
から WKWebView
の移行を今更ながらやっております。
個人アプリだとまず WKWebView
とか SFSafariViewController
などを
選ぶわけですが,昔からのアプリだとまだ UIWebView
だったり普通にありますよね・・・
UIWebView
の方は読み込み開始時に UIActivityIndicatorView
を出して,
読み込み完了時に消す(ぐるぐるのやつ)という実装やっていましたが,
WKWebView
は estimatedProgress
という読み込み具合の値が取れるので,
その値を見て NavigationBar
部分に UIProgressView
を表示するようにしました。
当時の実装だと 0.9 あたりで止まってそのあと非表示になるような感じだったので,
NavigationBar
の右端まで行ってるように見せたかったのでその修正をしました。
WKWebView の実装
今回の実装は GitHub にもあげましたので
気になる方がいらっしゃいましたらご覧下さい。
まずは WKWebView
を普通に実装します。
iOS 8 以降はこちらを使うように推奨されています。
もうほとんど対応がなくなったかもですが,
iOS 7 に対応する場合は別に UIWebView
の実装も必要です。
Xcode 9 から WKWebView
の addSubView
周りののコードで書かなくて
良くなったので少しコード減らせるかもしれないですね。(ほんの少しだけ)
せっかくなのでサンプル実装では IB を使ってみます。
コードは最低限書きます。
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
にするという実装。
実行すると下記のようになる。
0.9
あたりで止まって消えるように見える。1.0
まで行ってほしい・・・
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
部分をごっそり省く。実行すると下記のようになる。
ですよねーって感じ。1.0
にはなるんだけど UIProgressView
は残ったまま。
ここで見えなくなってほしい。
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
ぐらいで消える感じ。
これだと望んだ結果となった。
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 に最適化されてるって感じなのでしょうか・・・
iOS 11 からのプロジェクトってまだなかなかないのでしょうけど。
詳しい方いらっしゃったらご教授お願いいたします。