ウェブページを表示する画面を作っていたのですが、また見慣れないSwiftLintのワーニングが出たので、シリーズ続行です。
UIWebViewがdeprecatedになったので、WKWebViewを使うことにしました。
ページのロード中を示すインジケータの表示ですが、UIWebViewならUIWebViewDelegateを実装すれば開始と終了のイベントが取得できるので、丸いアニメーションインジケータを表示するのが簡単だと思います。WKWebViewでも、WKNavigationDelegateを実装すれば同様にコントロールできます。
(※ 今回のソースは全てSwift4で、最小限動くところまで削るためにターゲットをiOS11.0以降にしました。ご注意を。)
import UIKit
import WebKit
class SomeWeb4ViewController: UIViewController, WKNavigationDelegate {
@IBOutlet weak var webView: WKWebView!
@IBOutlet weak var indicatorView: UIActivityIndicatorView! //くるくるインジケータ
override func viewDidLoad() {
super.viewDidLoad()
indicatorView.hidesWhenStopped = true //くるくるしてないときは、非表示
//webページのロード
webView.navigationDelegate = self
let myURL = URL(string: "https://www.apple.com/")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
indicatorView.startAnimating() //くるくる開始
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
indicatorView.stopAnimating() //くるくる停止
}
}
まぁ、見たとおりです。WKNavigationDelegateで、Webページのロードの開始と停止のイベントを取得しています。処理もシンプルで分かりやすいですね。ただ、WKWebViewはKVOに対応しているので、もう少し気の利いた制御をするのがお約束なのでしょうか。そんな情報が多いです。そこで、それらを真似てナビゲーションバーの下に、横に伸びるインジケータ表示にしました。
import UIKit
import WebKit
class SomeWeb2ViewController: UIViewController {
@IBOutlet weak var webView: WKWebView!
var progressView = UIProgressView()
override func viewDidLoad() {
super.viewDidLoad()
//プログレスバー
let progFrame = CGRect(x: 0.0,
y: self.navigationController!.navigationBar.frame.size.height - 2.0,
width: self.navigationController!.navigationBar.frame.size.width,
height: 10.0)
progressView = UIProgressView(frame: progFrame)
progressView.progressViewStyle = .bar
self.navigationController?.navigationBar.addSubview(progressView)
//ここで、webViewのobserverを設定して
webView.addObserver(self, forKeyPath: "loading", options: .new, context: nil)
webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
//webページのロード
let myURL = URL(string: "https://www.apple.com/")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}
//ここで進捗を受信
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "estimatedProgress"{
progressView.setProgress(Float(webView.estimatedProgress), animated: true)
} else if keyPath == "loading"{
UIApplication.shared.isNetworkActivityIndicatorVisible = webView.isLoading
if webView.isLoading {
progressView.setProgress(0.1, animated: true) //プログレスの開始
} else {
progressView.setProgress(0.0, animated: false) //プログレスを消去
}
}
}
deinit {
webView.removeObserver(self, forKeyPath: "estimatedProgress")
webView.removeObserver(self, forKeyPath: "loading")
}
}
webViewに、"loading"と"estimatedProgress"のobserverを付加して、ロードの開始、進捗、終了を取得しています。ちなみにdeinitにある、observerの削除がないと落ちるとのことですが、11.0では落ちなかったです。だから不要、というわけでもなさそうですが。
もちろんこれでちゃんと動作するのですが、SwiftLintがワーニングを出力します。
・block_based_kvo
Block Based KVO Violation: Prefer the new block based KVO API with keypaths when using Swift 3.2 or later. (block_based_kvo)
new block based KVO API with keypaths ってなに?って感じですが、 どうやらWKWebViewに付加したobserverを func observeValue(){} で受信する方法は古いようです。
そこで、Adopting Cocoa Design Patternsの「Key-Value Observing」を参考にして、 NSKeyValueObservationを使った方法に書き換えてみました。
import UIKit
import WebKit
class SomeWeb3ViewController: UIViewController {
@IBOutlet weak var webView: WKWebView!
var progressView = UIProgressView()
var obsLoading: NSKeyValueObservation? //←これ
var obsEstimatedProgress: NSKeyValueObservation? //←これ
override func viewDidLoad() {
super.viewDidLoad()
//プログレスバー
let progFrame = CGRect(x: 0.0,
y: self.navigationController!.navigationBar.frame.size.height - 2.0,
width: UIScreen.main.bounds.size.width,
height: 10.0)
progressView = UIProgressView(frame: progFrame)
progressView.progressViewStyle = .bar
self.navigationController?.navigationBar.addSubview(progressView)
//ここから→
obsLoading = observe(\.webView.loading) { _, _ in
UIApplication.shared.isNetworkActivityIndicatorVisible = self.webView.isLoading
if self.webView.isLoading {
self.progressView.setProgress(0.1, animated: true) //プログレスの開始
} else {
self.progressView.setProgress(0.0, animated: false) //プログレスを消去
}
}
obsEstimatedProgress = observe(\.webView.estimatedProgress) { _, _ in
self.progressView.setProgress(Float(self.webView.estimatedProgress), animated: true)
}
//←ここまで
let myURL = URL(string: "https://www.apple.com/")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}
}
Swift4におけるKVOのベストプラクティスがよくわかっていないので、本当にこれが正しいのか自信がないのですが、とりあえずワーニングが消えて、ちゃんと動きました。内心、ちょっと怪しいとは感じています。ま、そのときはそのときで随時修正します。それにしても、なにこのバックスラッシュ。Swiftこわい。
♪♪♪
こちらもどうぞ。
凛としてSwift
凛としてSwift (血闘編ノ壱)
凛としてSwift (血闘編ノ弐)
凛としてSwift (血闘編ノ参)