#何が問題か?
WebView内のコンテンツがSPA(Single Page Application)で作られていると、ページ遷移しても期待したようにWKWebView
のdelegate
が呼ばれない場合があります。SPAは最初にhtmlを取得したらJavaScriptによって画面の書き換えを行うので、webページの読み込みが初回のみしか発生しないためです。
例えばwebコンテンツがページ遷移するたび、htmlのtitle
タグの情報を取得しnavigationItem.title
にセットする場合を考えます。
普通のwebページなら読み込み完了のタイミングで以下のようにtitle
をセットするので問題ありませんが、SPAだとこのdelegate
は初回のみしか呼ばれません。そのため最初にセットされたtitle
がnavigationItem
に残り続け、遷移先のtitle
はnavigationItem
に反映されません。
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
guard let title = webView.title else { return }
navigationItem.title = title
}
#どうするか?
今回の例では以下のふたつを組み合わせて対応できます。
-
viewDidLayoutSubviews
内でurl
の変更を監視する - SwiftからJavaScriptを実行して
title
を取得する
class ViewController: UIViewController {
var webView: WKWebView!
private var urlObserver: NSKeyValueObservation?
// SPAのwebページ遷移で呼ばれる
override func viewDidLayoutSubviews() {
urlObserver = webView.observe(\.url, options: .new) { [weak self] _, _ in
guard let self = self else { return }
self.setTitle()
}
}
private func setTitle() {
// jsのdocument.titleプロパティを実行
let script = "document.title"
webView.evaluateJavaScript(script) { (result, _) in
self.navigationItem.title = result as? String
}
}
}
extension ViewController: WKNavigationDelegate {
// 初回のWebView読み込み時によばれる
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
setTitle()
}
}
以上のようにtitle
を取得できますし、同じ要領でurl
も取得可能です。
他の場面においてもSPAで構成されたwebコンテンツでWKWebView
のdelegate
を使用したい場合は、適宜url
の監視をうまく使ってやるのが有効かもしれません。
##参考